OLD | NEW |
| (Empty) |
1 # Copyright (C) 2009 Google Inc. All rights reserved. | |
2 # | |
3 # Redistribution and use in source and binary forms, with or without | |
4 # modification, are permitted provided that the following conditions are | |
5 # met: | |
6 # | |
7 # * Redistributions of source code must retain the above copyright | |
8 # notice, this list of conditions and the following disclaimer. | |
9 # * Redistributions in binary form must reproduce the above | |
10 # copyright notice, this list of conditions and the following disclaimer | |
11 # in the documentation and/or other materials provided with the | |
12 # distribution. | |
13 # * Neither the name of Google Inc. nor the names of its | |
14 # contributors may be used to endorse or promote products derived from | |
15 # this software without specific prior written permission. | |
16 # | |
17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
28 | |
29 import os | |
30 import tempfile | |
31 import webkitpy.thirdparty.unittest2 as unittest | |
32 from webkitpy.common.net.credentials import Credentials | |
33 from webkitpy.common.system.executive import Executive | |
34 from webkitpy.common.system.outputcapture import OutputCapture | |
35 from webkitpy.common.system.user_mock import MockUser | |
36 from webkitpy.thirdparty.mock import Mock | |
37 from webkitpy.tool.mocktool import MockOptions | |
38 from webkitpy.common.system.executive_mock import MockExecutive | |
39 | |
40 | |
41 # FIXME: Other unit tests probably want this class. | |
42 class _TemporaryDirectory(object): | |
43 def __init__(self, **kwargs): | |
44 self._kwargs = kwargs | |
45 self._directory_path = None | |
46 | |
47 def __enter__(self): | |
48 self._directory_path = tempfile.mkdtemp(**self._kwargs) | |
49 return self._directory_path | |
50 | |
51 def __exit__(self, type, value, traceback): | |
52 os.rmdir(self._directory_path) | |
53 | |
54 | |
55 # Note: All tests should use this class instead of Credentials directly to avoid
using a real Executive. | |
56 class MockedCredentials(Credentials): | |
57 def __init__(self, *args, **kwargs): | |
58 if 'executive' not in kwargs: | |
59 kwargs['executive'] = MockExecutive() | |
60 Credentials.__init__(self, *args, **kwargs) | |
61 | |
62 | |
63 class CredentialsTest(unittest.TestCase): | |
64 example_security_output = """keychain: "/Users/test/Library/Keychains/login.
keychain" | |
65 class: "inet" | |
66 attributes: | |
67 0x00000007 <blob>="bugs.webkit.org (test@webkit.org)" | |
68 0x00000008 <blob>=<NULL> | |
69 "acct"<blob>="test@webkit.org" | |
70 "atyp"<blob>="form" | |
71 "cdat"<timedate>=0x32303039303832353233353231365A00 "20090825235216Z\000" | |
72 "crtr"<uint32>=<NULL> | |
73 "cusi"<sint32>=<NULL> | |
74 "desc"<blob>="Web form password" | |
75 "icmt"<blob>="default" | |
76 "invi"<sint32>=<NULL> | |
77 "mdat"<timedate>=0x32303039303930393137323635315A00 "20090909172651Z\000" | |
78 "nega"<sint32>=<NULL> | |
79 "path"<blob>=<NULL> | |
80 "port"<uint32>=0x00000000 | |
81 "prot"<blob>=<NULL> | |
82 "ptcl"<uint32>="htps" | |
83 "scrp"<sint32>=<NULL> | |
84 "sdmn"<blob>=<NULL> | |
85 "srvr"<blob>="bugs.webkit.org" | |
86 "type"<uint32>=<NULL> | |
87 password: "SECRETSAUCE" | |
88 """ | |
89 | |
90 def test_keychain_lookup_on_non_mac(self): | |
91 class FakeCredentials(MockedCredentials): | |
92 def _is_mac_os_x(self): | |
93 return False | |
94 credentials = FakeCredentials("bugs.webkit.org") | |
95 self.assertFalse(credentials._is_mac_os_x()) | |
96 self.assertEqual(credentials._credentials_from_keychain("foo"), ["foo",
None]) | |
97 | |
98 def test_security_output_parse(self): | |
99 credentials = MockedCredentials("bugs.webkit.org") | |
100 self.assertEqual(credentials._parse_security_tool_output(self.example_se
curity_output), ["test@webkit.org", "SECRETSAUCE"]) | |
101 | |
102 def test_security_output_parse_entry_not_found(self): | |
103 # FIXME: This test won't work if the user has a credential for foo.examp
le.com! | |
104 credentials = Credentials("foo.example.com") | |
105 if not credentials._is_mac_os_x(): | |
106 return # This test does not run on a non-Mac. | |
107 | |
108 # Note, we ignore the captured output because it is already covered | |
109 # by the test case CredentialsTest._assert_security_call (below). | |
110 outputCapture = OutputCapture() | |
111 outputCapture.capture_output() | |
112 self.assertIsNone(credentials._run_security_tool()) | |
113 outputCapture.restore_output() | |
114 | |
115 def _assert_security_call(self, username=None): | |
116 executive_mock = Mock() | |
117 credentials = MockedCredentials("example.com", executive=executive_mock) | |
118 | |
119 expected_logs = "Reading Keychain for example.com account and password.
Click \"Allow\" to continue...\n" | |
120 OutputCapture().assert_outputs(self, credentials._run_security_tool, [us
ername], expected_logs=expected_logs) | |
121 | |
122 security_args = ["/usr/bin/security", "find-internet-password", "-g", "-
s", "example.com"] | |
123 if username: | |
124 security_args += ["-a", username] | |
125 executive_mock.run_command.assert_called_with(security_args) | |
126 | |
127 def test_security_calls(self): | |
128 self._assert_security_call() | |
129 self._assert_security_call(username="foo") | |
130 | |
131 def test_credentials_from_environment(self): | |
132 credentials = MockedCredentials("example.com") | |
133 | |
134 saved_environ = os.environ.copy() | |
135 os.environ['WEBKIT_BUGZILLA_USERNAME'] = "foo" | |
136 os.environ['WEBKIT_BUGZILLA_PASSWORD'] = "bar" | |
137 username, password = credentials._credentials_from_environment() | |
138 self.assertEqual(username, "foo") | |
139 self.assertEqual(password, "bar") | |
140 os.environ = saved_environ | |
141 | |
142 def test_read_credentials_without_git_repo(self): | |
143 # FIXME: This should share more code with test_keyring_without_git_repo | |
144 class FakeCredentials(MockedCredentials): | |
145 def _is_mac_os_x(self): | |
146 return True | |
147 | |
148 def _credentials_from_keychain(self, username): | |
149 return ("test@webkit.org", "SECRETSAUCE") | |
150 | |
151 def _credentials_from_environment(self): | |
152 return (None, None) | |
153 | |
154 with _TemporaryDirectory(suffix="not_a_git_repo") as temp_dir_path: | |
155 credentials = FakeCredentials("bugs.webkit.org", cwd=temp_dir_path) | |
156 # FIXME: Using read_credentials here seems too broad as higher-prior
ity | |
157 # credential source could be affected by the user's environment. | |
158 self.assertEqual(credentials.read_credentials(), ("test@webkit.org",
"SECRETSAUCE")) | |
159 | |
160 | |
161 def test_keyring_without_git_repo(self): | |
162 # FIXME: This should share more code with test_read_credentials_without_
git_repo | |
163 class MockKeyring(object): | |
164 def get_password(self, host, username): | |
165 return "NOMNOMNOM" | |
166 | |
167 class FakeCredentials(MockedCredentials): | |
168 def _is_mac_os_x(self): | |
169 return True | |
170 | |
171 def _credentials_from_keychain(self, username): | |
172 return ("test@webkit.org", None) | |
173 | |
174 def _credentials_from_environment(self): | |
175 return (None, None) | |
176 | |
177 with _TemporaryDirectory(suffix="not_a_git_repo") as temp_dir_path: | |
178 credentials = FakeCredentials("fake.hostname", cwd=temp_dir_path, ke
yring=MockKeyring()) | |
179 # FIXME: Using read_credentials here seems too broad as higher-prior
ity | |
180 # credential source could be affected by the user's environment. | |
181 self.assertEqual(credentials.read_credentials(), ("test@webkit.org",
"NOMNOMNOM")) | |
182 | |
183 def test_keyring_without_git_repo_nor_keychain(self): | |
184 class MockKeyring(object): | |
185 def get_password(self, host, username): | |
186 return "NOMNOMNOM" | |
187 | |
188 class FakeCredentials(MockedCredentials): | |
189 def _credentials_from_keychain(self, username): | |
190 return (None, None) | |
191 | |
192 def _credentials_from_environment(self): | |
193 return (None, None) | |
194 | |
195 class FakeUser(MockUser): | |
196 @classmethod | |
197 def prompt(cls, message, repeat=1, raw_input=raw_input): | |
198 return "test@webkit.org" | |
199 | |
200 @classmethod | |
201 def prompt_password(cls, message, repeat=1, raw_input=raw_input): | |
202 raise AssertionError("should not prompt for password") | |
203 | |
204 with _TemporaryDirectory(suffix="not_a_git_repo") as temp_dir_path: | |
205 credentials = FakeCredentials("fake.hostname", cwd=temp_dir_path, ke
yring=MockKeyring()) | |
206 # FIXME: Using read_credentials here seems too broad as higher-prior
ity | |
207 # credential source could be affected by the user's environment. | |
208 self.assertEqual(credentials.read_credentials(FakeUser), ("test@webk
it.org", "NOMNOMNOM")) | |
OLD | NEW |