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