Skip to content

Commit

Permalink
Merge pull request #26690 from whiteinge/base64_decode
Browse files Browse the repository at this point in the history
Add execution module and state module to base64 encode/decode files
  • Loading branch information
Mike Place committed Aug 31, 2015
2 parents 6b7f3db + 29ac54d commit f0c9dff
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 3 deletions.
119 changes: 116 additions & 3 deletions salt/modules/hashutil.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,69 @@
import base64
import hashlib
import hmac
import StringIO

# Import third-party libs
import salt.utils
# Import Salt libs
import salt.exceptions
import salt.ext.six as six
import salt.utils


def digest(instr, checksum='md5'):
'''
Return a checksum digest for a string
instr
A string
checksum : ``md5``
The hashing algorithm to use to generate checksums. Valid options: md5,
sha256, sha512.
CLI Example:
.. code-block:: bash
salt '*' hashutil.digest 'get salted'
'''
hashing_funcs = {
'md5': __salt__['hashutil.md5_digest'],
'sha256': __salt__['hashutil.sha256_digest'],
'sha512': __salt__['hashutil.sha512_digest'],
}
hash_func = hashing_funcs.get(checksum)

if hash_func is None:
raise salt.exceptions.CommandExecutionError(
"Hash func '{0}' is not supported.".format(checksum))

return hash_func(instr)


def digest_file(infile, checksum='md5'):
'''
Return a checksum digest for a file
infile
A file path
checksum : ``md5``
The hashing algorithm to use to generate checksums. Wraps the
:py:func:`hashutil.digest <salt.modules.hashutil.digest>` execution
function.
CLI Example:
.. code-block:: bash
salt '*' hashutil.digest_file /path/to/file
'''
if not __salt__['file.file_exists'](infile):
raise salt.exceptions.CommandExecutionError(
"File path '{0}' not found.".format(infile))

with open(infile, 'rb') as f:
file_hash = __salt__['hashutil.digest'](f.read(), checksum)

return file_hash


def base64_b64encode(instr):
Expand Down Expand Up @@ -81,6 +140,39 @@ def base64_encodestring(instr):
return base64.encodestring(instr)


def base64_encodefile(fname):
'''
Read a file from the file system and return as a base64 encoded string
.. versionadded:: Boron
Pillar example:
.. code-block:: yaml
path:
to:
data: |
{{ salt.hashutil.base64_encodefile('/path/to/binary_file') | indent(6) }}
The :py:func:`file.decode <salt.states.file.decode>` state function can be
used to decode this data and write it to disk.
CLI Example:
.. code-block:: bash
salt '*' hashutil.base64_encodefile /path/to/binary_file
'''
encoded_f = StringIO.StringIO()

with open(fname, 'rb') as f:
base64.encode(f, encoded_f)

encoded_f.seek(0)
return encoded_f.read()


def base64_decodestring(instr):
'''
Decode a base64-encoded string using the "legacy" Python interface
Expand All @@ -91,7 +183,8 @@ def base64_decodestring(instr):
.. code-block:: bash
salt '*' hashutil.base64_decodestring 'Z2V0IHNhbHRlZA==\\n'
salt '*' hashutil.base64_decodestring instr='Z2V0IHNhbHRlZAo='
'''
if six.PY3:
b = salt.utils.to_bytes(instr)
Expand All @@ -103,6 +196,26 @@ def base64_decodestring(instr):
return base64.decodestring(instr)


def base64_decodefile(instr, outfile):
r'''
Decode a base64-encoded string and write the result to a file
.. versionadded:: 2015.2.0
CLI Example:
.. code-block:: bash
salt '*' hashutil.base64_decodefile instr='Z2V0IHNhbHRlZAo=' outfile='/path/to/binary_file'
'''
encoded_f = StringIO.StringIO(instr)

with open(outfile, 'wb') as f:
base64.decode(encoded_f, f)

return True


def md5_digest(instr):
'''
Generate an md5 hash of a given string
Expand Down
113 changes: 113 additions & 0 deletions salt/states/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -4610,3 +4610,116 @@ def mod_run_check_cmd(cmd, filename, **check_cmd_opts):

# No reason to stop, return True
return True


def decode(name,
encoded_data=None,
contents_pillar=None,
encoding_type='base64',
checksum='md5'):
'''
Decode an encoded file and write it to disk
.. versionadded:: Boron
name
Path of the file to be written.
encoded_data
The encoded file. Either this option or ``contents_pillar`` must be
specified.
contents_pillar
A Pillar path to the encoded file. Uses the same path syntax as
:py:func:`pillar.get <salt.modules.pillar.get>`. The
:py:func:`hashutil.base64_encodefile
<salt.modules.hashutil.base64_encodefile>` function can load encoded
content into Pillar. Either this option or ``encoded_data`` must be
specified.
encoding_type : ``base64``
The type of encoding.
checksum : ``md5``
The hashing algorithm to use to generate checksums. Wraps the
:py:func:`hashutil.digest <salt.modules.hashutil.digest>` execution
function.
Usage:
.. code-block:: yaml
write_base64_encoded_string_to_a_file:
file.decode:
- name: /tmp/new_file
- encoding_type: base64
- contents_pillar: mypillar:thefile
# or
write_base64_encoded_string_to_a_file:
file.decode:
- name: /tmp/new_file
- encoding_type: base64
- encoded_data: |
Z2V0IHNhbHRlZAo=
Be careful with multi-line strings that the YAML indentation is correct.
E.g.,
.. code-block:: yaml
write_base64_encoded_string_to_a_file:
file.decode:
- name: /tmp/new_file
- encoding_type: base64
- encoded_data: |
{{ salt.pillar.get('path:to:data') | indent(8) }}
'''
ret = {'name': name, 'changes': {}, 'result': False, 'comment': ''}

if not (encoded_data or contents_pillar):
raise CommandExecutionError("Specify either the 'encoded_data' or "
"'contents_pillar' argument.")
elif encoded_data and contents_pillar:
raise CommandExecutionError("Specify only one 'encoded_data' or "
"'contents_pillar' argument.")
elif encoded_data:
content = encoded_data
elif contents_pillar:
content = __salt__['pillar.get'](contents_pillar, False)
if content is False:
raise CommandExecutionError('Pillar data not found.')
else:
raise CommandExecutionError('No contents given.')

dest_exists = __salt__['file.file_exists'](name)
if dest_exists:
instr = __salt__['hashutil.base64_decodestring'](content)
insum = __salt__['hashutil.digest'](instr, checksum)
del instr # no need to keep in-memory after we have the hash
outsum = __salt__['hashutil.digest_file'](name, checksum)

if insum != outsum:
ret['changes'] = {
'old': outsum,
'new': insum,
}

if not ret['changes']:
ret['comment'] = 'File is in the correct state.'
ret['result'] = True

return ret

if __opts__['test'] is True:
ret['comment'] = 'File is set to be updated.'
ret['result'] = None
return ret

ret['result'] = __salt__['hashutil.base64_decodefile'](content, name)
ret['comment'] = 'File was updated.'

if not ret['changes']:
ret['changes'] = {
'old': None,
'new': __salt__['hashutil.digest_file'](name, checksum),
}

return ret

0 comments on commit f0c9dff

Please sign in to comment.