|
5 | 5 | import pytest |
6 | 6 |
|
7 | 7 | import salt.states.ssh_auth as ssh_auth |
8 | | -from tests.support.mock import MagicMock, patch |
| 8 | +from tests.support.mock import MagicMock, call, patch |
9 | 9 |
|
10 | 10 |
|
11 | 11 | @pytest.fixture |
12 | 12 | def configure_loader_modules(): |
13 | | - return {ssh_auth: {}} |
| 13 | + return {ssh_auth: {"__env__": "base"}} |
| 14 | + |
| 15 | + |
| 16 | +@pytest.fixture() |
| 17 | +def enc_types(): |
| 18 | + # This list is from sshd manual page of openssh 8.7 |
| 19 | + return [ |
| 20 | + |
| 21 | + "ecdsa-sha2-nistp256", |
| 22 | + "ecdsa-sha2-nistp384", |
| 23 | + "ecdsa-sha2-nistp521", |
| 24 | + |
| 25 | + "ssh-ed25519", |
| 26 | + "ssh-dss", |
| 27 | + "ssh-rsa", |
| 28 | + ] |
| 29 | + |
| 30 | + |
| 31 | +def test__present_test(enc_types): |
| 32 | + """ |
| 33 | + Test to verify if many encryption key types are accepted by ssh_auth._present_test |
| 34 | + """ |
| 35 | + user = "user" |
| 36 | + key = "1ABCDXXXXXXXXXXXXXXXXXXXXXXXXXXXX=" |
| 37 | + |
| 38 | + name_list = [] |
| 39 | + expected_calls = [] |
| 40 | + for enc in enc_types: |
| 41 | + name_list.append(f"{enc} {key} {comment}") |
| 42 | + expected_calls.append( |
| 43 | + call( |
| 44 | + user, |
| 45 | + key, |
| 46 | + enc, |
| 47 | + comment, |
| 48 | + [], |
| 49 | + config=".ssh/authorized_keys", |
| 50 | + fingerprint_hash_type=None, |
| 51 | + ) |
| 52 | + ) |
| 53 | + |
| 54 | + mock_check_key = MagicMock(return_value="update") |
| 55 | + |
| 56 | + with patch.dict(ssh_auth.__salt__, {"ssh.check_key": mock_check_key}): |
| 57 | + with patch.dict(ssh_auth.__opts__, {"test": True}): |
| 58 | + for name in name_list: |
| 59 | + ret = None, f"Key {key} for user {user} is set to be updated" |
| 60 | + assert ( |
| 61 | + ssh_auth._present_test( |
| 62 | + user, name, enc, comment, [], "", ".ssh/authorized_keys", None |
| 63 | + ) |
| 64 | + == ret |
| 65 | + ) |
| 66 | + mock_check_key.assert_has_calls(expected_calls) |
| 67 | + |
| 68 | + |
| 69 | +def test__absent_test(enc_types): |
| 70 | + """ |
| 71 | + Test to verify if many encryption key types are accepted by ssh_auth._absent_test |
| 72 | + """ |
| 73 | + user = "user" |
| 74 | + key = "1ABCDXXXXXXXXXXXXXXXXXXXXXXXXXXXX=" |
| 75 | + |
| 76 | + name_list = [] |
| 77 | + expected_calls = [] |
| 78 | + for enc in enc_types: |
| 79 | + name_list.append(f"{enc} {key} {comment}") |
| 80 | + expected_calls.append( |
| 81 | + call( |
| 82 | + user, |
| 83 | + key, |
| 84 | + enc, |
| 85 | + comment, |
| 86 | + [], |
| 87 | + config=".ssh/authorized_keys", |
| 88 | + fingerprint_hash_type=None, |
| 89 | + ) |
| 90 | + ) |
| 91 | + |
| 92 | + mock_check_key = MagicMock(return_value="update") |
| 93 | + |
| 94 | + with patch.dict(ssh_auth.__salt__, {"ssh.check_key": mock_check_key}): |
| 95 | + with patch.dict(ssh_auth.__opts__, {"test": True}): |
| 96 | + for name in name_list: |
| 97 | + ret = None, f"Key {key} for user {user} is set for removal" |
| 98 | + assert ( |
| 99 | + ssh_auth._absent_test( |
| 100 | + user, name, enc, comment, [], "", ".ssh/authorized_keys", None |
| 101 | + ) |
| 102 | + == ret |
| 103 | + ) |
| 104 | + mock_check_key.assert_has_calls(expected_calls) |
14 | 105 |
|
15 | 106 |
|
16 | 107 | def test_present(): |
@@ -44,6 +135,101 @@ def test_present(): |
44 | 135 | assert ssh_auth.present(name, user, source) == ret |
45 | 136 |
|
46 | 137 |
|
| 138 | +def test_present_with_source(): |
| 139 | + """ |
| 140 | + Test to check if ssh.set_auth_key_from_file is called with the correct parameters |
| 141 | + """ |
| 142 | + user = "user" |
| 143 | + source = "salt://fakefile" |
| 144 | + config = ".ssh/authorized_keys" |
| 145 | + |
| 146 | + mock_cp_get_url = MagicMock(return_value=True) |
| 147 | + mock_auth_key_from_file = MagicMock(return_value="new") |
| 148 | + |
| 149 | + with patch.dict( |
| 150 | + ssh_auth.__salt__, |
| 151 | + { |
| 152 | + "cp.get_url": mock_cp_get_url, |
| 153 | + "ssh.set_auth_key_from_file": mock_auth_key_from_file, |
| 154 | + }, |
| 155 | + ): |
| 156 | + with patch.dict(ssh_auth.__opts__, {"test": False}): |
| 157 | + name = "Call Without Options" |
| 158 | + ret = { |
| 159 | + "name": name, |
| 160 | + "changes": {name: "New"}, |
| 161 | + "result": True, |
| 162 | + "comment": f"The authorized host key {name} for user {user} was added", |
| 163 | + } |
| 164 | + assert ssh_auth.present(name, user, source=source) == ret |
| 165 | + mock_auth_key_from_file.assert_called_with( |
| 166 | + "user", |
| 167 | + "salt://fakefile", |
| 168 | + config=".ssh/authorized_keys", |
| 169 | + saltenv="base", |
| 170 | + fingerprint_hash_type=None, |
| 171 | + options=None, |
| 172 | + ) |
| 173 | + name = "Call With Options" |
| 174 | + ret = { |
| 175 | + "name": name, |
| 176 | + "changes": {name: "New"}, |
| 177 | + "result": True, |
| 178 | + "comment": f"The authorized host key {name} for user {user} was added", |
| 179 | + } |
| 180 | + assert ( |
| 181 | + ssh_auth.present(name, user, source=source, options=["no-pty"]) == ret |
| 182 | + ) |
| 183 | + mock_auth_key_from_file.assert_called_with( |
| 184 | + "user", |
| 185 | + "salt://fakefile", |
| 186 | + config=".ssh/authorized_keys", |
| 187 | + saltenv="base", |
| 188 | + fingerprint_hash_type=None, |
| 189 | + options=["no-pty"], |
| 190 | + ) |
| 191 | + |
| 192 | + |
| 193 | +def test_present_different_key_types_are_accepted(enc_types): |
| 194 | + """ |
| 195 | + Test to verify if many encryption key types are accepted by ssh_auth.present |
| 196 | + """ |
| 197 | + user = "user" |
| 198 | + key = "1ABCDXXXXXXXXXXXXXXXXXXXXXXXXXXXX=" |
| 199 | + |
| 200 | + name_list = [] |
| 201 | + expected_calls = [] |
| 202 | + for enc in enc_types: |
| 203 | + name_list.append(f"{enc} {key} {comment}") |
| 204 | + expected_calls.append( |
| 205 | + call( |
| 206 | + user, |
| 207 | + key, |
| 208 | + enc, |
| 209 | + comment, |
| 210 | + [], |
| 211 | + "", |
| 212 | + ".ssh/authorized_keys", |
| 213 | + None, |
| 214 | + ) |
| 215 | + ) |
| 216 | + |
| 217 | + with patch.object( |
| 218 | + ssh_auth, "_present_test", return_value=[None, "Key is to be added"] |
| 219 | + ) as mock__present_test: |
| 220 | + # it's tested with `test: True` because this already tests the fullkey regexp |
| 221 | + with patch.dict(ssh_auth.__opts__, {"test": True}): |
| 222 | + for name in name_list: |
| 223 | + ret = { |
| 224 | + "name": name, |
| 225 | + "changes": {}, |
| 226 | + "result": None, |
| 227 | + "comment": "Key is to be added", |
| 228 | + } |
| 229 | + assert ssh_auth.present(name, user) == ret |
| 230 | + mock__present_test.assert_has_calls(expected_calls) |
| 231 | + |
| 232 | + |
47 | 233 | def test_absent(): |
48 | 234 | """ |
49 | 235 | Test to verifies that the specified SSH key is absent. |
@@ -80,6 +266,78 @@ def test_absent(): |
80 | 266 | assert ssh_auth.absent(name, user, source) == ret |
81 | 267 |
|
82 | 268 |
|
| 269 | +def test_absent_with_source(): |
| 270 | + """ |
| 271 | + Test to check if ssh.rm_auth_key_from_file is called with the correct parameters |
| 272 | + """ |
| 273 | + user = "user" |
| 274 | + source = "salt://fakefile" |
| 275 | + config = ".ssh/authorized_keys" |
| 276 | + |
| 277 | + mock_cp_get_url = MagicMock(return_value=True) |
| 278 | + mock_auth_key_from_file = MagicMock(return_value="Key removed") |
| 279 | + |
| 280 | + with patch.dict( |
| 281 | + ssh_auth.__salt__, |
| 282 | + { |
| 283 | + "cp.get_url": mock_cp_get_url, |
| 284 | + "ssh.rm_auth_key_from_file": mock_auth_key_from_file, |
| 285 | + }, |
| 286 | + ): |
| 287 | + with patch.dict(ssh_auth.__opts__, {"test": False}): |
| 288 | + name = "Call Absent With Source" |
| 289 | + ret = { |
| 290 | + "name": name, |
| 291 | + "changes": {name: "Removed"}, |
| 292 | + "result": True, |
| 293 | + "comment": "Key removed", |
| 294 | + } |
| 295 | + assert ssh_auth.absent(name, user, source=source) == ret |
| 296 | + mock_auth_key_from_file.assert_called_with( |
| 297 | + "user", |
| 298 | + "salt://fakefile", |
| 299 | + ".ssh/authorized_keys", |
| 300 | + saltenv="base", |
| 301 | + fingerprint_hash_type=None, |
| 302 | + ) |
| 303 | + |
| 304 | + |
| 305 | +def test_absent_with_different_key_types_are_accepted(enc_types): |
| 306 | + """ |
| 307 | + Test to verify if many encription key types are accepted |
| 308 | + """ |
| 309 | + user = "user" |
| 310 | + key = "1ABCDXXXXXXXXXXXXXXXXXXXXXXXXXXXX=" |
| 311 | + |
| 312 | + name_list = [] |
| 313 | + expected_calls = [] |
| 314 | + for enc in enc_types: |
| 315 | + name_list.append(f"{enc} {key} {comment}") |
| 316 | + expected_calls.append( |
| 317 | + call( |
| 318 | + user, |
| 319 | + key, |
| 320 | + config=".ssh/authorized_keys", |
| 321 | + fingerprint_hash_type=None, |
| 322 | + ) |
| 323 | + ) |
| 324 | + |
| 325 | + mock_rm_test = MagicMock(return_value="Key removed") |
| 326 | + |
| 327 | + with patch.dict(ssh_auth.__salt__, {"ssh.rm_auth_key": mock_rm_test}): |
| 328 | + # it's tested with `test: False` because the fullkey regexp is only in the actual execution |
| 329 | + with patch.dict(ssh_auth.__opts__, {"test": False}): |
| 330 | + for name in name_list: |
| 331 | + ret = { |
| 332 | + "name": name, |
| 333 | + "changes": {key: "Removed"}, |
| 334 | + "result": True, |
| 335 | + "comment": "Key removed", |
| 336 | + } |
| 337 | + assert ssh_auth.absent(name, user) == ret |
| 338 | + mock_rm_test.assert_has_calls(expected_calls) |
| 339 | + |
| 340 | + |
83 | 341 | def test_manage(): |
84 | 342 | """ |
85 | 343 | Test to verifies that the specified SSH key is absent. |
|
0 commit comments