committer_auth_unittest.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #!/usr/bin/env python
  2. #
  3. # Copyright (C) 2011 Apple Inc. All rights reserved.
  4. #
  5. # Redistribution and use in source and binary forms, with or without
  6. # modification, are permitted provided that the following conditions
  7. # are met:
  8. # 1. Redistributions of source code must retain the above copyright
  9. # notice, this list of conditions and the following disclaimer.
  10. # 2. Redistributions in binary form must reproduce the above copyright
  11. # notice, this list of conditions and the following disclaimer in the
  12. # documentation and/or other materials provided with the distribution.
  13. #
  14. # THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
  15. # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  16. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  17. # DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR
  18. # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  19. # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  20. # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  21. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  22. # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  23. # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. import StringIO
  25. import __builtin__
  26. import buildbot.status.web.auth
  27. import contextlib
  28. import os
  29. import unittest
  30. from committer_auth import CommitterAuth
  31. # This subclass of StringIO supports the context manager protocol so it works
  32. # with "with" statements, just like real files.
  33. class CMStringIO(StringIO.StringIO):
  34. def __enter__(self):
  35. return self
  36. def __exit__(self, exception, value, traceback):
  37. self.close()
  38. @contextlib.contextmanager
  39. def open_override(func):
  40. original_open = __builtin__.open
  41. __builtin__.open = func
  42. yield
  43. __builtin__.open = original_open
  44. class CommitterAuthTest(unittest.TestCase):
  45. def setUp(self):
  46. self.auth = CommitterAuth('path/to/auth.json')
  47. self.auth.open_auth_json_file = self.fake_auth_json_file
  48. self.auth.open_webkit_committers_file = self.fake_committers_file
  49. self.auth.open_trac_credentials_file = self.fake_htdigest_file
  50. def fake_open_function(self, expected_filename):
  51. def fake_open(name, mode='r'):
  52. self.fake_open_was_called = True
  53. self.assertEqual(expected_filename, name)
  54. return fake_open
  55. def test_authentication_success(self):
  56. self.assertTrue(self.auth.authenticate('committer@webkit.org', 'committerpassword'))
  57. self.assertEqual('', self.auth.errmsg())
  58. self.assertTrue(self.auth.authenticate('committer2@example.com', 'committer2password'))
  59. self.assertEqual('', self.auth.errmsg())
  60. def test_committer_without_trac_credentials_fails(self):
  61. self.assertFalse(self.auth.authenticate('committer3@webkit.org', 'committer3password'))
  62. self.assertEqual('Invalid username/password', self.auth.errmsg())
  63. def test_fail_to_open_auth_json_file(self):
  64. def raise_IOError():
  65. raise IOError(2, 'No such file or directory', 'path/to/auth.json')
  66. auth = CommitterAuth('path/to/auth.json')
  67. auth.open_auth_json_file = raise_IOError
  68. self.assertFalse(auth.authenticate('committer@webkit.org', 'committerpassword'))
  69. self.assertEqual('Error opening auth.json file: No such file or directory', auth.errmsg())
  70. def test_fail_to_open_trac_credentials_file(self):
  71. def raise_IOError():
  72. raise IOError(2, 'No such file or directory', 'path/to/trac/credentials')
  73. self.auth.open_trac_credentials_file = raise_IOError
  74. self.assertFalse(self.auth.authenticate('committer@webkit.org', 'committerpassword'))
  75. self.assertEqual('Error opening Trac credentials file: No such file or directory', self.auth.errmsg())
  76. def test_fail_to_open_webkit_committers_file(self):
  77. def raise_IOError():
  78. raise IOError(2, 'No such file or directory', 'path/to/webkit/committers')
  79. self.auth.open_webkit_committers_file = raise_IOError
  80. self.assertFalse(self.auth.authenticate('committer@webkit.org', 'committerpassword'))
  81. self.assertEqual('Error opening WebKit committers file: No such file or directory', self.auth.errmsg())
  82. def test_implements_IAuth(self):
  83. self.assertTrue(buildbot.status.web.auth.IAuth.implementedBy(CommitterAuth))
  84. def test_invalid_auth_json_file(self):
  85. auth = CommitterAuth('path/to/auth.json')
  86. auth.open_auth_json_file = self.invalid_auth_json_file
  87. self.assertFalse(auth.authenticate('committer@webkit.org', 'committerpassword'))
  88. self.assertEqual('Error parsing auth.json file: No JSON object could be decoded', auth.errmsg())
  89. def test_invalid_committers_file(self):
  90. self.auth.open_webkit_committers_file = self.invalid_committers_file
  91. self.assertFalse(self.auth.authenticate('committer@webkit.org', 'committerpassword'))
  92. self.assertEqual('Error parsing WebKit committers file', self.auth.errmsg())
  93. def test_invalid_trac_credentials_file(self):
  94. self.auth.open_trac_credentials_file = self.invalid_htdigest_file
  95. self.assertFalse(self.auth.authenticate('committer@webkit.org', 'committerpassword'))
  96. self.assertEqual('Error parsing Trac credentials file', self.auth.errmsg())
  97. def test_missing_auth_json_keys(self):
  98. auth = CommitterAuth('path/to/auth.json')
  99. auth.open_auth_json_file = lambda: CMStringIO('{ "trac_credentials": "path/to/trac/credentials" }')
  100. self.assertFalse(auth.authenticate('committer@webkit.org', 'committerpassword'))
  101. self.assertEqual('auth.json file is missing "webkit_committers" key', auth.errmsg())
  102. auth.open_auth_json_file = lambda: CMStringIO('{ "webkit_committers": "path/to/webkit/committers" }')
  103. auth.open_webkit_committers_file = self.fake_committers_file
  104. self.assertFalse(auth.authenticate('committer@webkit.org', 'committerpassword'))
  105. self.assertEqual('auth.json file is missing "trac_credentials" key', auth.errmsg())
  106. def test_open_auth_json_file(self):
  107. auth = CommitterAuth('path/to/auth.json')
  108. self.fake_open_was_called = False
  109. with open_override(self.fake_open_function(auth.auth_json_filename())):
  110. auth.open_auth_json_file()
  111. self.assertTrue(self.fake_open_was_called)
  112. def test_open_trac_credentials_file(self):
  113. auth = CommitterAuth('path/to/auth.json')
  114. auth.trac_credentials_filename = lambda: 'trac credentials filename'
  115. self.fake_open_was_called = False
  116. with open_override(self.fake_open_function(auth.trac_credentials_filename())):
  117. auth.open_trac_credentials_file()
  118. self.assertTrue(self.fake_open_was_called)
  119. def test_open_webkit_committers_file(self):
  120. auth = CommitterAuth('path/to/auth.json')
  121. auth.webkit_committers_filename = lambda: 'webkit committers filename'
  122. self.fake_open_was_called = False
  123. with open_override(self.fake_open_function(auth.webkit_committers_filename())):
  124. auth.open_webkit_committers_file()
  125. self.assertTrue(self.fake_open_was_called)
  126. def test_non_committer_fails(self):
  127. self.assertFalse(self.auth.authenticate('noncommitter@example.com', 'noncommitterpassword'))
  128. self.assertEqual('Invalid username/password', self.auth.errmsg())
  129. def test_trac_credentials_filename(self):
  130. self.assertEqual('path/to/trac/credentials', self.auth.trac_credentials_filename())
  131. def test_unknown_user_fails(self):
  132. self.assertFalse(self.auth.authenticate('nobody@example.com', 'nobodypassword'))
  133. self.assertEqual('Invalid username/password', self.auth.errmsg())
  134. def test_username_is_prefix_of_valid_user(self):
  135. self.assertFalse(self.auth.authenticate('committer@webkit.orgg', 'committerpassword'))
  136. self.assertEqual('Invalid username/password', self.auth.errmsg())
  137. def test_webkit_committers(self):
  138. self.assertEqual(['committer@webkit.org', 'committer2@example.com', 'committer3@webkit.org'], self.auth.webkit_committers())
  139. def test_webkit_committers_filename(self):
  140. self.assertEqual('path/to/webkit/committers', self.auth.webkit_committers_filename())
  141. def test_wrong_password_fails(self):
  142. self.assertFalse(self.auth.authenticate('committer@webkit.org', 'wrongpassword'))
  143. self.assertEqual('Invalid username/password', self.auth.errmsg())
  144. def fake_auth_json_file(self):
  145. return CMStringIO("""{
  146. "trac_credentials": "path/to/trac/credentials",
  147. "webkit_committers": "path/to/webkit/committers"
  148. }""")
  149. def invalid_auth_json_file(self):
  150. return CMStringIO('~!@#$%^&*()_+')
  151. def fake_committers_file(self):
  152. return CMStringIO("""[groups]
  153. group1 = user@example.com,user2@example.com
  154. group2 = user3@example.com
  155. group3 =
  156. group4 =
  157. webkit = committer@webkit.org,committer2@example.com,committer3@webkit.org
  158. [service:/]
  159. * = r
  160. """)
  161. def invalid_committers_file(self):
  162. return CMStringIO("""[groups]
  163. [[groups2]
  164. """)
  165. def fake_htdigest_file(self):
  166. return CMStringIO("""committer@webkit.org:Mac OS Forge:761c8dcb7d9b5908007ed142f62fe73a
  167. committer2@example.com:Mac OS Forge:faeee69acc2e49af3a0dbb15bd593ef4
  168. noncommitter@example.com:Mac OS Forge:b99aa7ad32306a654ca4d57839fde9c1
  169. """)
  170. def invalid_htdigest_file(self):
  171. return CMStringIO("""committer@webkit.org:Mac OS Forge:761c8dcb7d9b5908007ed142f62fe73a
  172. committer2@example.com:Mac OS Forge:faeee69acc2e49af3a0dbb15bd593ef4
  173. noncommitter@example.com:Mac OS Forge:b99aa7ad32306a654ca4d57839fde9c1
  174. committer4@example.com:Mac OS Forge:::
  175. """)
  176. if __name__ == '__main__':
  177. unittest.main()