86dc3c09ac
* Fix vault --ask-vault-pass with no tty 2.4.0 added a check for isatty() that would skip setting up interactive vault password prompts if not running on a tty. But... getpass.getpass() will fallback to reading from stdin if it gets that far without a tty. Since 2.4.0 skipped the interactive prompts / getpass.getpass() in that case, it would never get a chance to fall back to stdin. So if 'echo $VAULT_PASSWORD| ansible-playbook --ask-vault-pass site.yml' was ran without a tty (ie, from a jenkins job or via the vagrant ansible provisioner) the 2.4 behavior was different than 2.3. 2.4 would never read the password from stdin, resulting in a vault password error like: ERROR! Attempting to decrypt but no vault secrets found Fix is just to always call the interactive password prompts based on getpass.getpass() on --ask-vault-pass or --vault-id @prompt and let getpass sort it out. * up test_prompt_no_tty to expect prompt with no tty We do call the PromptSecret class if there is no tty, but we are back to expecting it to read from stdin in that case. * Fix logic for when to auto-prompt vault pass If --ask-vault-pass is used, then pretty much always prompt. If it is not used, then prompt if there are no other vault ids provided and 'auto_prompt==True'. Fixes vagrant bug https://github.com/hashicorp/vagrant/issues/9033 Fixes #30993
177 lines
7.8 KiB
Python
177 lines
7.8 KiB
Python
# (c) 2017, Adrian Likins <alikins@redhat.com>
|
|
#
|
|
# This file is part of Ansible
|
|
#
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
# Make coding more python3-ish
|
|
from __future__ import (absolute_import, division, print_function)
|
|
__metaclass__ = type
|
|
|
|
from ansible.compat.tests import unittest
|
|
from ansible.compat.tests.mock import patch
|
|
from units.mock.vault_helper import TextVaultSecret
|
|
|
|
from ansible import errors
|
|
from ansible.cli.vault import VaultCLI
|
|
|
|
|
|
# TODO: make these tests assert something, likely by verifing
|
|
# mock calls
|
|
|
|
|
|
class TestVaultCli(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tty_patcher = patch('ansible.cli.sys.stdin.isatty', return_value=False)
|
|
self.mock_isatty = self.tty_patcher.start()
|
|
|
|
def tearDown(self):
|
|
self.tty_patcher.stop()
|
|
|
|
def test_parse_empty(self):
|
|
cli = VaultCLI([])
|
|
self.assertRaisesRegexp(errors.AnsibleOptionsError,
|
|
'.*Missing required action.*',
|
|
cli.parse)
|
|
|
|
# FIXME: something weird seems to be afoot when parsing actions
|
|
# cli = VaultCLI(args=['view', '/dev/null/foo', 'mysecret3'])
|
|
# will skip '/dev/null/foo'. something in cli.CLI.set_action() ?
|
|
# maybe we self.args gets modified in a loop?
|
|
def test_parse_view_file(self):
|
|
cli = VaultCLI(args=['ansible-vault', 'view', '/dev/null/foo'])
|
|
cli.parse()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
def test_view_missing_file_no_secret(self, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = []
|
|
cli = VaultCLI(args=['ansible-vault', 'view', '/dev/null/foo'])
|
|
cli.parse()
|
|
self.assertRaisesRegexp(errors.AnsibleOptionsError,
|
|
"A vault password is required to use Ansible's Vault",
|
|
cli.run)
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
def test_encrypt_missing_file_no_secret(self, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = []
|
|
cli = VaultCLI(args=['ansible-vault', 'encrypt', '/dev/null/foo'])
|
|
cli.parse()
|
|
self.assertRaisesRegexp(errors.AnsibleOptionsError,
|
|
"A vault password is required to use Ansible's Vault",
|
|
cli.run)
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_encrypt(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'encrypt', '/dev/null/foo'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_encrypt_string(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'encrypt_string',
|
|
'some string to encrypt'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
@patch('ansible.cli.vault.display.prompt', return_value='a_prompt')
|
|
def test_encrypt_string_prompt(self, mock_display, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault',
|
|
'encrypt_string',
|
|
'--prompt',
|
|
'some string to encrypt'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
@patch('ansible.cli.vault.sys.stdin.read', return_value='This is data from stdin')
|
|
def test_encrypt_string_stdin(self, mock_stdin_read, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault',
|
|
'encrypt_string',
|
|
'--stdin-name',
|
|
'the_var_from_stdin',
|
|
'-'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_encrypt_string_names(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'encrypt_string',
|
|
'--name', 'foo1',
|
|
'--name', 'foo2',
|
|
'some string to encrypt'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_encrypt_string_more_args_than_names(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'encrypt_string',
|
|
'--name', 'foo1',
|
|
'some string to encrypt',
|
|
'other strings',
|
|
'a few more string args'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_create(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'create', '/dev/null/foo'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_edit(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'edit', '/dev/null/foo'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_decrypt(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'decrypt', '/dev/null/foo'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_view(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'view', '/dev/null/foo'])
|
|
cli.parse()
|
|
cli.run()
|
|
|
|
@patch('ansible.cli.vault.VaultCLI.setup_vault_secrets')
|
|
@patch('ansible.cli.vault.VaultEditor')
|
|
def test_rekey(self, mock_vault_editor, mock_setup_vault_secrets):
|
|
mock_setup_vault_secrets.return_value = [('default', TextVaultSecret('password'))]
|
|
cli = VaultCLI(args=['ansible-vault', 'rekey', '/dev/null/foo'])
|
|
cli.parse()
|
|
cli.run()
|