test_security.py (6688B)
1 import unittest 2 from unittest.mock import mock_open, patch 3 4 import bcrypt 5 6 import aoh.security 7 from aoh.playbook import Playbook 8 from aoh.security import ( 9 PasswordFileError, 10 get_required_passwords, 11 validate_aoh_password, 12 validate_args, 13 ) 14 15 16 class TestSecurityGetRequiredPasswords(unittest.TestCase): 17 def test_default_options(self): 18 pb = Playbook('main.yml', {}) 19 20 self.assertEqual( 21 set(get_required_passwords(pb, [])), 22 set(), 23 ) 24 self.assertEqual( 25 set(get_required_passwords(pb, ['--ask-pass'])), 26 {'connection_password'}, 27 ) 28 self.assertEqual( 29 set(get_required_passwords(pb, ['--ask-pass', '-K'])), 30 {'connection_password', 'become_password'}, 31 ) 32 33 34 def test_aoh_password(self): 35 pb = Playbook('main.yml', {'password': True}) 36 37 self.assertEqual( 38 set(get_required_passwords(pb, [])), 39 {'aoh_password'}, 40 ) 41 self.assertEqual( 42 set(get_required_passwords(pb, ['--ask-pass'])), 43 {'aoh_password', 'connection_password'}, 44 ) 45 self.assertEqual( 46 set(get_required_passwords(pb, ['--ask-pass', '-K'])), 47 {'aoh_password', 'connection_password', 'become_password'}, 48 ) 49 50 51 @patch.object(aoh.security, 'get_config_values', return_value={ 52 'DEFAULT_ASK_PASS': False, 53 'DEFAULT_BECOME_ASK_PASS': True, 54 'DEFAULT_ASK_VAULT_PASS': False, 55 }) 56 def test_config_file(self, _): 57 pb = Playbook('main.yml', {}) 58 59 self.assertEqual( 60 set(get_required_passwords(pb, [])), 61 {'become_password'}, 62 ) 63 self.assertEqual( 64 set(get_required_passwords(pb, ['--ask-pass'])), 65 {'become_password', 'connection_password'}, 66 ) 67 self.assertEqual( 68 set(get_required_passwords(pb, ['--ask-pass', '-K'])), 69 {'become_password', 'connection_password'}, 70 ) 71 72 73 @patch.object(aoh.security, 'get_config_values', return_value={ 74 'DEFAULT_ASK_PASS': False, 75 'DEFAULT_BECOME_ASK_PASS': True, 76 'DEFAULT_ASK_VAULT_PASS': False, 77 }) 78 def test_aoh_password_and_config_file(self, _): 79 pb = Playbook('main.yml', {'password': True}) 80 81 self.assertEqual( 82 set(get_required_passwords(pb, [])), 83 {'aoh_password', 'become_password'}, 84 ) 85 self.assertEqual( 86 set(get_required_passwords(pb, ['--ask-pass'])), 87 {'aoh_password', 'become_password', 'connection_password'}, 88 ) 89 self.assertEqual( 90 set(get_required_passwords(pb, ['--ask-pass', '-K'])), 91 {'aoh_password', 'become_password', 'connection_password'}, 92 ) 93 94 95 class TestSecurityValidateAohPassword(unittest.TestCase): 96 hash = bcrypt.hashpw(b'hunter2', bcrypt.gensalt()).decode() 97 98 def test_no_password_required(self): 99 pb = Playbook('main.yml', {'password': False}) 100 101 self.assertTrue(validate_aoh_password(pb, 'asdf')) 102 self.assertTrue(validate_aoh_password(pb, None)) 103 104 105 def test_null_password(self): 106 pb = Playbook('main.yml', {'password': True}) 107 108 self.assertFalse(validate_aoh_password(pb, None)) 109 110 111 @patch.object(aoh.security, 'open', mock_open(read_data='other.yml: foo')) 112 def test_missing_hash(self): 113 pb = Playbook('main.yml', {'password': True}) 114 115 with self.assertRaisesRegex(PasswordFileError, 'not found'): 116 validate_aoh_password(pb, 'asdf') 117 118 119 @patch.object(aoh.security, 'open', mock_open(read_data='main.yml: foo')) 120 def test_bad_hash(self): 121 pb = Playbook('main.yml', {'password': True}) 122 123 with self.assertRaisesRegex(PasswordFileError, 'Invalid bcrypt hash'): 124 validate_aoh_password(pb, 'asdf') 125 126 127 @patch.object(aoh.security, 'open', 128 mock_open(read_data=f'main.yml: {hash}')) 129 def test_bad_password(self): 130 pb = Playbook('main.yml', {'password': True}) 131 132 self.assertFalse(validate_aoh_password(pb, 'asdf')) 133 134 135 @patch.object(aoh.security, 'open', 136 mock_open(read_data=f'main.yml: {hash}')) 137 def test_good_password(self): 138 pb = Playbook('main.yml', {'password': True}) 139 140 self.assertTrue(validate_aoh_password(pb, 'hunter2')) 141 142 143 class TestSecurityValidateArgs(unittest.TestCase): 144 def test_default_options(self): 145 pb = Playbook('main.yml', {}) 146 147 self.assertTrue(validate_args(pb, ['--check'])) 148 self.assertTrue(validate_args(pb, ['-t', 'foo'])) 149 self.assertTrue(validate_args(pb, ['--tags=bar'])) 150 151 self.assertFalse(validate_args(pb, ['--verbose', '--version'])) 152 self.assertFalse(validate_args(pb, ['-e', 'foo=bar'])) 153 self.assertFalse(validate_args(pb, ['--check', '-v'])) 154 155 pb.allow_opts = ['--version'] 156 157 self.assertTrue(validate_args(pb, ['--check'])) 158 self.assertTrue(validate_args(pb, ['-t', 'foo'])) 159 self.assertTrue(validate_args(pb, ['--tags=bar'])) 160 161 self.assertFalse(validate_args(pb, ['--verbose', '--version'])) 162 self.assertFalse(validate_args(pb, ['-e', 'foo=bar'])) 163 self.assertFalse(validate_args(pb, ['--check', '-v'])) 164 165 166 def test_allowed_options(self): 167 pb = Playbook('main.yml', {}) 168 169 self.assertFalse(validate_args(pb, ['--verbose', '--check'])) 170 171 pb.allow_opts = ['--verbose'] 172 173 self.assertTrue(validate_args(pb, ['--verbose', '--check'])) 174 175 176 def test_blocked_options(self): 177 pb = Playbook('main.yml', {}) 178 179 self.assertTrue(validate_args(pb, ['--diff', '--check'])) 180 181 pb.block_opts = ['--check'] 182 183 self.assertFalse(validate_args(pb, ['--diff', '--check'])) 184 185 186 def test_jinja(self): 187 pb = Playbook('main.yml', {}) 188 189 self.assertFalse(validate_args(pb, ['-t', '{{x}}'])) 190 self.assertFalse(validate_args(pb, ['-t', '{% x %}'])) 191 self.assertFalse(validate_args(pb, ['-t', '\u007b\x25 x %}'])) 192 193 pb.jinja_args = True 194 195 self.assertTrue(validate_args(pb, ['-t', '{{x}}'])) 196 self.assertTrue(validate_args(pb, ['-t', '{% x %}'])) 197 self.assertTrue(validate_args(pb, ['-t', '\u007b\x25 x %}'])) 198 199 self.assertFalse(validate_args(pb, ['--verbose', '--version'])) 200 self.assertFalse(validate_args(pb, ['-e', 'foo=bar'])) 201 self.assertFalse(validate_args(pb, ['--check', '-v'])) 202 203 204 def test_unsupported_options(self): 205 pb = Playbook('main.yml', {}) 206 207 self.assertFalse(validate_args(pb, ['--step'])) 208 209 pb.allow_opts = ['--step'] 210 211 self.assertFalse(validate_args(pb, ['--step']))