win_copy: fix for copying encrypted file without pass (#31084)

* win_copy: fix for copying encrypted file without pass

* fix pep8 issue

* reduced the diff and fixed some minor issues
This commit is contained in:
Jordan Borean 2017-10-04 06:51:57 +11:00 committed by Matt Davis
parent 72237b63e7
commit bba941cd5b
5 changed files with 63 additions and 10 deletions

View file

@ -15,14 +15,14 @@ import tempfile
import traceback import traceback
import zipfile import zipfile
from ansible.errors import AnsibleError from ansible.errors import AnsibleError, AnsibleFileNotFound
from ansible.module_utils._text import to_bytes, to_native, to_text from ansible.module_utils._text import to_bytes, to_native, to_text
from ansible.module_utils.parsing.convert_bool import boolean from ansible.module_utils.parsing.convert_bool import boolean
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.utils.hashing import checksum from ansible.utils.hashing import checksum
def _walk_dirs(topdir, base_path=None, local_follow=False, trailing_slash_detector=None, checksum_check=False): def _walk_dirs(topdir, loader, base_path=None, local_follow=False, trailing_slash_detector=None, checksum_check=False):
""" """
Walk a filesystem tree returning enough information to copy the files. Walk a filesystem tree returning enough information to copy the files.
This is similar to the _walk_dirs function in ``copy.py`` but returns This is similar to the _walk_dirs function in ``copy.py`` but returns
@ -30,6 +30,7 @@ def _walk_dirs(topdir, base_path=None, local_follow=False, trailing_slash_detect
a local file if wanted. a local file if wanted.
:arg topdir: The directory that the filesystem tree is rooted at :arg topdir: The directory that the filesystem tree is rooted at
:arg loader: The self._loader object from ActionBase
:kwarg base_path: The initial directory structure to strip off of the :kwarg base_path: The initial directory structure to strip off of the
files for the destination directory. If this is None (the default), files for the destination directory. If this is None (the default),
the base_path is set to ``top_dir``. the base_path is set to ``top_dir``.
@ -100,7 +101,7 @@ def _walk_dirs(topdir, base_path=None, local_follow=False, trailing_slash_detect
if os.path.islink(filepath): if os.path.islink(filepath):
# Dereference the symlnk # Dereference the symlnk
real_file = os.path.realpath(filepath) real_file = loader.get_real_file(os.path.realpath(filepath), decrypt=True)
if local_follow and os.path.isfile(real_file): if local_follow and os.path.isfile(real_file):
# Add the file pointed to by the symlink # Add the file pointed to by the symlink
r_files['files'].append( r_files['files'].append(
@ -115,11 +116,12 @@ def _walk_dirs(topdir, base_path=None, local_follow=False, trailing_slash_detect
r_files['symlinks'].append({"src": os.readlink(filepath), "dest": dest_filepath}) r_files['symlinks'].append({"src": os.readlink(filepath), "dest": dest_filepath})
else: else:
# Just a normal file # Just a normal file
real_file = loader.get_real_file(filepath, decrypt=True)
r_files['files'].append( r_files['files'].append(
{ {
"src": filepath, "src": real_file,
"dest": dest_filepath, "dest": dest_filepath,
"checksum": _get_local_checksum(checksum_check, filepath) "checksum": _get_local_checksum(checksum_check, real_file)
} }
) )
@ -336,7 +338,7 @@ class ActionModule(ActionBase):
content = self._task.args.get('content', None) content = self._task.args.get('content', None)
dest = self._task.args.get('dest', None) dest = self._task.args.get('dest', None)
remote_src = boolean(self._task.args.get('remote_src', False), strict=False) remote_src = boolean(self._task.args.get('remote_src', False), strict=False)
follow = boolean(self._task.args.get('follow', False), strict=False) local_follow = boolean(self._task.args.get('local_follow', False), strict=False)
force = boolean(self._task.args.get('force', True), strict=False) force = boolean(self._task.args.get('force', True), strict=False)
result['src'] = source result['src'] = source
@ -412,7 +414,7 @@ class ActionModule(ActionBase):
result['operation'] = 'folder_copy' result['operation'] = 'folder_copy'
# Get a list of the files we want to replicate on the remote side # Get a list of the files we want to replicate on the remote side
source_files = _walk_dirs(source, local_follow=follow, source_files = _walk_dirs(source, self._loader, local_follow=local_follow,
trailing_slash_detector=self._connection._shell.path_has_trailing_slash, trailing_slash_detector=self._connection._shell.path_has_trailing_slash,
checksum_check=force) checksum_check=force)
@ -426,6 +428,14 @@ class ActionModule(ActionBase):
else: else:
result['operation'] = 'file_copy' result['operation'] = 'file_copy'
# If the local file does not exist, get_real_file() raises AnsibleFileNotFound
try:
source_full = self._loader.get_real_file(source, decrypt=True)
except AnsibleFileNotFound as e:
result['failed'] = True
result['msg'] = "could not find src=%s, %s" % (source_full, to_text(e))
return result
original_basename = os.path.basename(source) original_basename = os.path.basename(source)
result['original_basename'] = original_basename result['original_basename'] = original_basename
@ -440,16 +450,16 @@ class ActionModule(ActionBase):
filename = os.path.basename(unix_path) filename = os.path.basename(unix_path)
check_dest = os.path.dirname(unix_path) check_dest = os.path.dirname(unix_path)
file_checksum = _get_local_checksum(force, source) file_checksum = _get_local_checksum(force, source_full)
source_files['files'].append( source_files['files'].append(
dict( dict(
src=source, src=source_full,
dest=filename, dest=filename,
checksum=file_checksum checksum=file_checksum
) )
) )
result['checksum'] = file_checksum result['checksum'] = file_checksum
result['size'] = os.path.getsize(to_bytes(source, errors='surrogate_or_strict')) result['size'] = os.path.getsize(to_bytes(source_full, errors='surrogate_or_strict'))
# find out the files/directories/symlinks that we need to copy to the server # find out the files/directories/symlinks that we need to copy to the server
query_args = self._task.args.copy() query_args = self._task.args.copy()

View file

@ -0,0 +1,6 @@
$ANSIBLE_VAULT;1.1;AES256
65653164323866373138353632323531393664393563633665373635623763353561386431373366
3232353263363034313136663062623336663463373966320a333763323032646463386432626161
36386330356637666362396661653935653064623038333031653335626164376465353235303636
3335616231663838620a303632343938326538656233393562303162343261383465623261646664
33613932343461626339333832363930303962633364303736376634396364643861

View file

@ -0,0 +1,5 @@
This directory contains some files that have been encrypted with ansible-vault.
This is to test out the decrypt parameter in win_copy.
The password is: password

View file

@ -0,0 +1,6 @@
$ANSIBLE_VAULT;1.1;AES256
30353665333635633433356261616636356130386330363962386533303566313463383734373532
3933643234323638623939613462346361313431363939370a303532656338353035346661353965
34656231633238396361393131623834316262306533663838336362366137306562646561383766
6363373965633337640a373666336461613337346131353564383134326139616561393664663563
3431

View file

@ -26,6 +26,32 @@
register: fail_missing_parent_dir register: fail_missing_parent_dir
failed_when: "'Destination directory ' + test_win_copy_path + '\\missing-dir does not exist' not in fail_missing_parent_dir.msg" failed_when: "'Destination directory ' + test_win_copy_path + '\\missing-dir does not exist' not in fail_missing_parent_dir.msg"
- name: fail to copy an encrypted file without the password set
win_copy:
src: '{{role_path}}/files-vault/vault-file'
dest: '{{test_win_copy_path}}\file'
register: fail_copy_encrypted_file
ignore_errors: yes # weird failed_when doesn't work in this case
- name: assert failure message when copying an encrypted file without the password set
assert:
that:
- fail_copy_encrypted_file|failed
- fail_copy_encrypted_file.msg == 'A vault password or secret must be specified to decrypt {{role_path}}/files-vault/vault-file'
- name: fail to copy a directory with an encrypted file without the password
win_copy:
src: '{{role_path}}/files-vault'
dest: '{{test_win_copy_path}}'
register: fail_copy_directory_with_enc_file
ignore_errors: yes
- name: assert failure message when copying a directory that contains an encrypted file without the password set
assert:
that:
- fail_copy_directory_with_enc_file|failed
- fail_copy_directory_with_enc_file.msg == 'A vault password or secret must be specified to decrypt {{role_path}}/files-vault/vault-file'
- name: copy with content (check mode) - name: copy with content (check mode)
win_copy: win_copy:
content: a content: a