PageRenderTime 27ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/testing/hooks/pre_commit/test_check_owners.py

http://gvn.googlecode.com/
Python | 333 lines | 292 code | 10 blank | 31 comment | 0 complexity | 55f1629dfb53be5a1524042a9903c07f MD5 | raw file
Possible License(s): Apache-2.0
  1. #! /usr/bin/python2.4
  2. # Copyright 2007 Google Inc.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. """check_owners.py unittest"""
  16. import unittest
  17. import svn.fs
  18. import svn.repos
  19. import gvn.hooks.info
  20. from gvn.hooks.pre_commit.check_owners import RunHook
  21. import testing.hooks.common
  22. SUPER_GROUP = "super-group"
  23. """
  24. Our testdata/test-checkin1 tree looks like this:
  25. test-checkin1
  26. test-checkin1/file1
  27. test-checkin1/B
  28. test-checkin1/B/dir
  29. test-checkin1/B/dir/fileB_dir
  30. test-checkin1/B/fileB
  31. test-checkin1/C
  32. test-checkin1/C/fileC
  33. test-checkin1/D
  34. test-checkin1/D/fileD
  35. The relevant portion of public/projects/greek-tree looks like this:
  36. public/projects/greek-tree/ +
  37. OWNERS < "user1 \n group: group1"
  38. A
  39. A/B
  40. A/B/OWNERS < "set noparen \n userAB \n userAB2"
  41. A/B/E
  42. A/B/F
  43. A/C/OWNERS < "file" (unsupported for now)
  44. A/D
  45. A/D/OWNERS < "groupAD" (missing "group:", so looks like a username)
  46. A/D/G
  47. A/D/G/OWNERS < "bad owners syntax ADG" (ignored)
  48. Depending on which piece of it we try to check in, we trigger different
  49. allow rules. See below for which users belong to which groups.
  50. """
  51. class TestOwners(testing.hooks.common.HookTestCase):
  52. def setUp(self):
  53. # HookTestCase creates and sets a _logger object based on whether
  54. # GVN_TEST_DEBUG is exported or not
  55. testing.hooks.common.HookTestCase.setUp(self)
  56. # create group pickle to use as our group file
  57. user_groups= {
  58. "user1": [ "group1", "group2", "group3" ],
  59. "user2": [ "group1", "group2" ],
  60. "user3": [ "group1" ],
  61. "user4": [ ],
  62. "user5": [ "group5" ],
  63. "userAB2": [ "group1" ],
  64. "userAD": [ "groupAD" ],
  65. "adminuser": [ SUPER_GROUP ],
  66. }
  67. for user in user_groups:
  68. self.userdb.WriteUserGroups(user, user_groups[user])
  69. def testNoOwner(self):
  70. """check a commit outside of OWNERS controlled tree.
  71. """
  72. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  73. "public/dir", "user1"), 0)
  74. def testGoodOwner(self):
  75. """check an OWNERs commit with correct owner.
  76. """
  77. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  78. "public/projects/greek-tree", "user1"), 0)
  79. def testUserNotInOwner(self):
  80. """check an OWNERS commit with incorrect owner.
  81. """
  82. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  83. "public/projects/greek-tree", "baduser"),
  84. "baduser not authorized in any OWNERS file")
  85. def testGroupOwner(self):
  86. """check an OWNERS commit with a group allowed owner (group1).
  87. """
  88. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  89. "public/projects/greek-tree", "user2"), 0)
  90. def testBadGroupOwner(self):
  91. """check an OWNERS commit with an owner in the group file without perms.
  92. """
  93. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  94. "public/projects/greek-tree", "user5"),
  95. "user5 not authorized in any OWNERS file")
  96. def testInheritGoodOwner(self):
  97. """check an inherited OWNERs commit with correct owner.
  98. """
  99. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  100. "public/projects/greek-tree/A", "user1"), 0)
  101. def testInheritGroupOwner(self):
  102. """check an inherited OWNERS commit with a group allowed owner (group1).
  103. """
  104. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  105. "public/projects/greek-tree/A", "user2"), 0)
  106. def testBadOwnerInherit(self):
  107. """check an inherited OWNERS commit with a disallowed owner.
  108. """
  109. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  110. "public/projects/greek-tree/A", "baduser"),
  111. "baduser not authorized in any OWNERS file")
  112. def testBadGroupOwnerInherit(self):
  113. """check an inherited OWNERS commit with a group file user without perms.
  114. """
  115. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  116. "public/projects/greek-tree/A", "user5"),
  117. "user5 not authorized in any OWNERS file")
  118. def testNoParentGoodOwner(self):
  119. """check an OWNER inside a file with noparent.
  120. """
  121. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  122. "public/projects/greek-tree/A/B", "userAB"), 0)
  123. def testNoParentOwner(self):
  124. """check that noparent works for denying users higher up.
  125. """
  126. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  127. "public/projects/greek-tree/A/B", "user1"),
  128. "user1 not authorized in any OWNERS file")
  129. def testNoParentGroupOwner(self):
  130. """check that noparent works for denying groups higher up.
  131. """
  132. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  133. "public/projects/greek-tree/A/B", "user2"),
  134. "user2 not authorized in any OWNERS file")
  135. def testBadGroupSyntax(self):
  136. """check that group OWNERShip is not granted by group without group prefix.
  137. """
  138. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  139. "public/projects/greek-tree/A/D", "userAD"),
  140. "userAD not authorized in any OWNERS file")
  141. def testBadOwnerSyntaxGoodUser(self):
  142. """check that a syntax error in OWNERS doesn't reject an otherwise ok user.
  143. """
  144. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  145. "public/projects/greek-tree/A/D/G", "user1"), 0)
  146. def testBadOwnerSyntaxBadUser(self):
  147. """check that a syntax error in OWNERS doesn't mistakenly allow a user.
  148. """
  149. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  150. "public/projects/greek-tree/A/D/G", "userAD"),
  151. "userAD not authorized in any OWNERS file")
  152. def testGvnApprove(self):
  153. """check an OWNERS commit with incorrect owner but with correct approve.
  154. """
  155. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  156. "public/projects/greek-tree", "baduser", "user1"), 0)
  157. def testGvnApproveSuperuser(self):
  158. """check that superuser can approve even though not in OWNERS"""
  159. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  160. "public/projects/greek-tree", "baduser",
  161. "adminuser"),
  162. 0)
  163. def testBadSuperuser(self):
  164. """check that superuser can't commit when not in OWNERS"""
  165. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  166. "public/projects/greek-tree",
  167. "adminuser", approve_user=None),
  168. "adminuser not authorized in any OWNERS file")
  169. def testBadGvnApprove(self):
  170. """check an OWNERS submit with incorrect owner but with incorrect approve.
  171. """
  172. self.assertHookResult(self._TestPaths(RunHook, "testdata",
  173. "public/projects/greek-tree", "baduser", "user4"),
  174. "baduser not authorized in any OWNERS file")
  175. def testMixedAllowDisallow(self):
  176. """do a mixed allowed/disallow submit: user2 ok in /, but not in A/B
  177. """
  178. self.assertHookResult(self._TestPaths(RunHook, "testdata/test-checkin1",
  179. "public/projects/greek-tree/A", "user2"),
  180. "user2 not authorized in any OWNERS file")
  181. def testMixedAllowDisallow2(self):
  182. """do a mixed allowed/disallow submit: userAB ok in / & A/B, but not in A/D
  183. """
  184. self.assertHookResult(self._TestPaths(RunHook, "testdata/test-checkin1",
  185. "public/projects/greek-tree/A", "userAB"),
  186. "userAB not authorized in any OWNERS file")
  187. def testMixedAllow(self):
  188. """do a dual allow with different files allowed by different OWNERS files
  189. """
  190. self.assertHookResult(self._TestPaths(RunHook, "testdata/test-checkin1",
  191. "public/projects/greek-tree/A", "userAB2"), 0)
  192. def testGoodOwnerProp(self):
  193. """check an OWNERs prop set with correct owner and no upper restriction
  194. """
  195. self.assertHookResult(self._TestProp(RunHook, "proptest", "propvalue",
  196. "user1", "public/projects/greek-tree"), 0)
  197. def testPropUserNotInOwner(self):
  198. """check an OWNERS propset with incorrect owner.
  199. """
  200. self.assertHookResult(self._TestProp(RunHook, "proptest", "propvalue",
  201. "baduser", "public/projects/greek-tree"),
  202. "baduser not authorized in any OWNERS file")
  203. def testPropUserInOwner(self):
  204. """check an OWNERS propset with correct owner.
  205. """
  206. self.assertHookResult(self._TestProp(RunHook, "proptest", "propvalue",
  207. "userAB", "public/projects/greek-tree/A/B"), 0)
  208. def testPropUserInParentOwner(self):
  209. """check propset with correct owner for the parent but not current dir.
  210. This test is a bit tricky, when setting properties on a directory, we
  211. have special code that checks the OWNERS in that same directory and not
  212. OWNERS in the parent trees.
  213. This is why this test is meant to fail.
  214. """
  215. self.assertHookResult(self._TestProp(RunHook, "proptest", "propvalue",
  216. "user1", "public/projects/greek-tree/A/B"),
  217. "user1 not authorized in any OWNERS file")
  218. def testSnapshotWithOwners(self):
  219. """check that a non-OWNER of a project can still snapshot from it
  220. This is to avoid the following bug:
  221. 0. trunk/OWNERS forbids me from modifying trunk
  222. 1. create changebranch from trunk, mail for approval
  223. - //changes/epg/foo/trunk does not exist, so no OWNERS, so allow
  224. 2. gvn snap more changes to my changebranch
  225. - //changes/epg/foo/trunk *does* exist, and so does
  226. //changes/epg/foo/trunk/OWNERS, so I get locked out!
  227. """
  228. # Mock up a HookInfo.
  229. class mock_userdb:
  230. def UserInGroup(self, user, group):
  231. return False
  232. hi = gvn.hooks.info.HookInfo(['pre-commit', 'unused-repo', 'unused-txn'],
  233. userdb=mock_userdb())
  234. hi._author = 'basil'
  235. hi._head_root = object()
  236. hi._txn = object()
  237. # Mock svn.fs.node_prop for hi.project_config's gvn:project search.
  238. def mock_node_prop(root, path, propname, pool=None):
  239. return None
  240. # Mock svn.fs.txn_prop for RunHook's gvn:change check.
  241. def mock_txn_prop(txn, propname, pool=None):
  242. return None
  243. # Mock svn.fs.check_path for RunHook.
  244. def mock_check_path(root, path, pool=None):
  245. if path.endswith('OWNERS'):
  246. return svn.core.svn_node_file
  247. # Our test modification path is a directory.
  248. return svn.core.svn_node_dir
  249. # Mock svn.core.Stream and svn.fs.file_contents to return an
  250. # OWNERS list excluding our intrepid hacker, basil.
  251. class mock_Stream:
  252. def __init__(self, data): self.data = data
  253. def read(self): return self.data
  254. def mock_file_contents(root, path, pool=None):
  255. return 'sybil\n'
  256. class mock_path_change_t:
  257. change_kind = svn.fs.path_change_modify
  258. # Save the originals...
  259. orig_node_prop = svn.fs.node_prop
  260. orig_txn_prop = svn.fs.txn_prop
  261. orig_check_path = svn.fs.check_path
  262. orig_Stream = svn.core.Stream
  263. orig_file_contents = svn.fs.file_contents
  264. try:
  265. # ...and install the mocks.
  266. svn.fs.node_prop = mock_node_prop
  267. svn.fs.txn_prop = mock_txn_prop
  268. svn.fs.check_path = mock_check_path
  269. svn.core.Stream = mock_Stream
  270. svn.fs.file_contents = mock_file_contents
  271. # First, check that basil cannot submit to the project.
  272. hi._paths_changed = {'/trunk': mock_path_change_t()}
  273. self.assertEqual(RunHook(hi, self._logger), 'basil not authorized in'
  274. ' any OWNERS file for trunk, sorry')
  275. # Reset for next test.
  276. hi._prefix_changed = None
  277. # Now, check that basil can snapshot it.
  278. hi._paths_changed = {'/changes/basil/test/trunk': mock_path_change_t()}
  279. self.assertEqual(RunHook(hi, self._logger), 0)
  280. finally:
  281. svn.fs.node_prop = orig_node_prop
  282. svn.fs.txn_prop = orig_txn_prop
  283. svn.fs.check_path = orig_check_path
  284. svn.core.Stream = orig_Stream
  285. svn.fs.file_contents = orig_file_contents