aoh

A tool for running Ansible playbooks with `curl <url> | python` commands
git clone https://git.ashermorgan.net/aoh/
Log | Files | Refs | README

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']))