diff options
Diffstat (limited to 'lib/ansible/cli/vault.py')
-rw-r--r-- | lib/ansible/cli/vault.py | 114 |
1 files changed, 53 insertions, 61 deletions
diff --git a/lib/ansible/cli/vault.py b/lib/ansible/cli/vault.py index 1b0ae67db2..8e561e284f 100644 --- a/lib/ansible/cli/vault.py +++ b/lib/ansible/cli/vault.py @@ -1,20 +1,6 @@ # (c) 2014, James Tanner <tanner.jc@gmail.com> -# -# 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/>. -# -# ansible-vault is a script that encrypts/decrypts YAML files. See -# https://docs.ansible.com/playbooks_vault.html for more details. +# Copyright: (c) 2018, Ansible Project +# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) from __future__ import (absolute_import, division, print_function) __metaclass__ = type @@ -22,8 +8,9 @@ __metaclass__ = type import os import sys -from ansible.cli import CLI from ansible import constants as C +from ansible import context +from ansible.cli import CLI from ansible.errors import AnsibleOptionsError from ansible.module_utils._text import to_text, to_bytes from ansible.parsing.dataloader import DataLoader @@ -75,7 +62,7 @@ class VaultCLI(CLI): if self.action in self.can_output: self.parser.add_option('--output', default=None, dest='output_file', help='output file name for encrypt or decrypt; use - for stdout', - action="callback", callback=CLI.unfrack_path, type='string') + action="callback", callback=self.unfrack_path, type='string') # options specific to self.actions if self.action == "create": @@ -109,9 +96,9 @@ class VaultCLI(CLI): action='store', type='string', help='the vault id used to encrypt (required if more than vault-id is provided)') - def parse(self): + def init_parser(self): - self.parser = CLI.base_parser( + self.parser = super(VaultCLI, self).init_parser( vault_opts=True, vault_rekey_opts=True, usage="usage: %%prog [%s] [options] [vaultfile.yml]" % "|".join(sorted(self.VALID_ACTIONS)), @@ -121,18 +108,21 @@ class VaultCLI(CLI): self.set_action() - super(VaultCLI, self).parse() - self.validate_conflicts(vault_opts=True, vault_rekey_opts=True) + return self.parser - display.verbosity = self.options.verbosity + def post_process_args(self, options, args): + options, args = super(VaultCLI, self).post_process_args(options, args) + self.validate_conflicts(options, vault_opts=True, vault_rekey_opts=True) - if self.options.vault_ids: - for vault_id in self.options.vault_ids: + display.verbosity = options.verbosity + + if options.vault_ids: + for vault_id in options.vault_ids: if u';' in vault_id: raise AnsibleOptionsError("'%s' is not a valid vault id. The character ';' is not allowed in vault ids" % vault_id) if self.action not in self.can_output: - if len(self.args) == 0: + if not args: raise AnsibleOptionsError("Vault requires at least one filename as a parameter") else: # This restriction should remain in place until it's possible to @@ -140,17 +130,19 @@ class VaultCLI(CLI): # to create an encrypted file that can't be read back in. But in # the meanwhile, "cat a b c|ansible-vault encrypt --output x" is # a workaround. - if self.options.output_file and len(self.args) > 1: + if options.output_file and len(args) > 1: raise AnsibleOptionsError("At most one input file may be used with the --output option") if self.action == 'encrypt_string': - if '-' in self.args or len(self.args) == 0 or self.options.encrypt_string_stdin_name: + if '-' in args or not args or options.encrypt_string_stdin_name: self.encrypt_string_read_stdin = True # TODO: prompting from stdin and reading from stdin seem mutually exclusive, but verify that. - if self.options.encrypt_string_prompt and self.encrypt_string_read_stdin: + if options.encrypt_string_prompt and self.encrypt_string_read_stdin: raise AnsibleOptionsError('The --prompt option is not supported if also reading input from stdin') + return options, args + def run(self): super(VaultCLI, self).run() loader = DataLoader() @@ -158,7 +150,7 @@ class VaultCLI(CLI): # set default restrictive umask old_umask = os.umask(0o077) - vault_ids = self.options.vault_ids + vault_ids = list(context.CLIARGS['vault_ids']) # there are 3 types of actions, those that just 'read' (decrypt, view) and only # need to ask for a password once, and those that 'write' (create, encrypt) that @@ -171,26 +163,25 @@ class VaultCLI(CLI): # TODO: instead of prompting for these before, we could let VaultEditor # call a callback when it needs it. if self.action in ['decrypt', 'view', 'rekey', 'edit']: - vault_secrets = self.setup_vault_secrets(loader, - vault_ids=vault_ids, - vault_password_files=self.options.vault_password_files, - ask_vault_pass=self.options.ask_vault_pass) + vault_secrets = self.setup_vault_secrets(loader, vault_ids=vault_ids, + vault_password_files=list(context.CLIARGS['vault_password_files']), + ask_vault_pass=context.CLIARGS['ask_vault_pass']) if not vault_secrets: raise AnsibleOptionsError("A vault password is required to use Ansible's Vault") if self.action in ['encrypt', 'encrypt_string', 'create']: encrypt_vault_id = None - # no --encrypt-vault-id self.options.encrypt_vault_id for 'edit' + # no --encrypt-vault-id context.CLIARGS['encrypt_vault_id'] for 'edit' if self.action not in ['edit']: - encrypt_vault_id = self.options.encrypt_vault_id or C.DEFAULT_VAULT_ENCRYPT_IDENTITY + encrypt_vault_id = context.CLIARGS['encrypt_vault_id'] or C.DEFAULT_VAULT_ENCRYPT_IDENTITY vault_secrets = None vault_secrets = \ self.setup_vault_secrets(loader, vault_ids=vault_ids, - vault_password_files=self.options.vault_password_files, - ask_vault_pass=self.options.ask_vault_pass, + vault_password_files=context.CLIARGS['vault_password_files'], + ask_vault_pass=context.CLIARGS['ask_vault_pass'], create_new_password=True) if len(vault_secrets) > 1 and not encrypt_vault_id: @@ -209,7 +200,7 @@ class VaultCLI(CLI): self.encrypt_secret = encrypt_secret[1] if self.action in ['rekey']: - encrypt_vault_id = self.options.encrypt_vault_id or C.DEFAULT_VAULT_ENCRYPT_IDENTITY + encrypt_vault_id = context.CLIARGS['encrypt_vault_id'] or C.DEFAULT_VAULT_ENCRYPT_IDENTITY # print('encrypt_vault_id: %s' % encrypt_vault_id) # print('default_encrypt_vault_id: %s' % default_encrypt_vault_id) @@ -218,18 +209,18 @@ class VaultCLI(CLI): new_vault_ids = [] if encrypt_vault_id: new_vault_ids = default_vault_ids - if self.options.new_vault_id: - new_vault_ids.append(self.options.new_vault_id) + if context.CLIARGS['new_vault_id']: + new_vault_ids.append(context.CLIARGS['new_vault_id']) new_vault_password_files = [] - if self.options.new_vault_password_file: - new_vault_password_files.append(self.options.new_vault_password_file) + if context.CLIARGS['new_vault_password_file']: + new_vault_password_files.append(context.CLIARGS['new_vault_password_file']) new_vault_secrets = \ self.setup_vault_secrets(loader, vault_ids=new_vault_ids, vault_password_files=new_vault_password_files, - ask_vault_pass=self.options.ask_vault_pass, + ask_vault_pass=context.CLIARGS['ask_vault_pass'], create_new_password=True) if not new_vault_secrets: @@ -257,14 +248,14 @@ class VaultCLI(CLI): def execute_encrypt(self): ''' encrypt the supplied file using the provided vault secret ''' - if len(self.args) == 0 and sys.stdin.isatty(): + if not context.CLIARGS['args'] and sys.stdin.isatty(): display.display("Reading plaintext input from stdin", stderr=True) - for f in self.args or ['-']: + for f in context.CLIARGS['args'] or ['-']: # Fixme: use the correct vau self.editor.encrypt_file(f, self.encrypt_secret, vault_id=self.encrypt_vault_id, - output_file=self.options.output_file) + output_file=context.CLIARGS['output_file']) if sys.stdout.isatty(): display.display("Encryption successful", stderr=True) @@ -296,10 +287,10 @@ class VaultCLI(CLI): # remove the non-option '-' arg (used to indicate 'read from stdin') from the candidate args so # we don't add it to the plaintext list - args = [x for x in self.args if x != '-'] + args = [x for x in context.CLIARGS['args'] if x != '-'] # We can prompt and read input, or read from stdin, but not both. - if self.options.encrypt_string_prompt: + if context.CLIARGS['encrypt_string_prompt']: msg = "String to encrypt: " name = None @@ -332,20 +323,21 @@ class VaultCLI(CLI): b_plaintext = to_bytes(stdin_text) # defaults to None - name = self.options.encrypt_string_stdin_name + name = context.CLIARGS['encrypt_string_stdin_name'] b_plaintext_list.append((b_plaintext, self.FROM_STDIN, name)) # use any leftover args as strings to encrypt # Try to match args up to --name options - if hasattr(self.options, 'encrypt_string_names') and self.options.encrypt_string_names: - name_and_text_list = list(zip(self.options.encrypt_string_names, args)) + if context.CLIARGS.get('encrypt_string_names', False): + name_and_text_list = list(zip(context.CLIARGS['encrypt_string_names'], args)) # Some but not enough --name's to name each var if len(args) > len(name_and_text_list): # Trying to avoid ever showing the plaintext in the output, so this warning is vague to avoid that. display.display('The number of --name options do not match the number of args.', stderr=True) - display.display('The last named variable will be "%s". The rest will not have names.' % self.options.encrypt_string_names[-1], + display.display('The last named variable will be "%s". The rest will not have' + ' names.' % context.CLIARGS['encrypt_string_names'][-1], stderr=True) # Add the rest of the args without specifying a name @@ -419,11 +411,11 @@ class VaultCLI(CLI): def execute_decrypt(self): ''' decrypt the supplied file using the provided vault secret ''' - if len(self.args) == 0 and sys.stdin.isatty(): + if not context.CLIARGS['args'] and sys.stdin.isatty(): display.display("Reading ciphertext input from stdin", stderr=True) - for f in self.args or ['-']: - self.editor.decrypt_file(f, output_file=self.options.output_file) + for f in context.CLIARGS['args'] or ['-']: + self.editor.decrypt_file(f, output_file=context.CLIARGS['output_file']) if sys.stdout.isatty(): display.display("Decryption successful", stderr=True) @@ -431,21 +423,21 @@ class VaultCLI(CLI): def execute_create(self): ''' create and open a file in an editor that will be encrypted with the provided vault secret when closed''' - if len(self.args) > 1: + if len(context.CLIARGS['args']) > 1: raise AnsibleOptionsError("ansible-vault create can take only one filename argument") - self.editor.create_file(self.args[0], self.encrypt_secret, + self.editor.create_file(context.CLIARGS['args'][0], self.encrypt_secret, vault_id=self.encrypt_vault_id) def execute_edit(self): ''' open and decrypt an existing vaulted file in an editor, that will be encrypted again when closed''' - for f in self.args: + for f in context.CLIARGS['args']: self.editor.edit_file(f) def execute_view(self): ''' open, decrypt and view an existing vaulted file using a pager using the supplied vault secret ''' - for f in self.args: + for f in context.CLIARGS['args']: # Note: vault should return byte strings because it could encrypt # and decrypt binary files. We are responsible for changing it to # unicode here because we are displaying it and therefore can make @@ -456,7 +448,7 @@ class VaultCLI(CLI): def execute_rekey(self): ''' re-encrypt a vaulted file with a new secret, the previous secret is required ''' - for f in self.args: + for f in context.CLIARGS['args']: # FIXME: plumb in vault_id, use the default new_vault_secret for now self.editor.rekey_file(f, self.new_encrypt_secret, self.new_encrypt_vault_id) |