Repository: ansible/ansible

Issue:
ansible_sudo_pass for numeric passwords
Hi, there is a bug with python types, when we use numeric passwords in inventory file:

```
  File "/usr/lib/pymodules/python2.7/ansible/runner/connection_plugins/ssh.py", line 352, in exec_command
    stdin.write(self.runner.sudo_pass + '\n')
TypeError: unsupported operand type(s) for +: 'int' and 'str'
```

`self.runner.sudo_pass` interpreted as integer and concatenation fails. :)



Discussion:

Resolving patch:
diff --git a/lib/ansible/callbacks.py b/lib/ansible/callbacks.py
--- a/lib/ansible/callbacks.py
+++ b/lib/ansible/callbacks.py
@@ -458,11 +458,11 @@ def on_unreachable(self, host, results):
         if type(results) == dict:
             item = results.get('item', None)
             if isinstance(item, unicode):
-                item = utils.to_bytes(item)
+                item = utils.unicode.to_bytes(item)
             results = basic.json_dict_unicode_to_bytes(results)
         else:
-            results = utils.to_bytes(results)
-        host = utils.to_bytes(host)
+            results = utils.unicode.to_bytes(results)
+        host = utils.unicode.to_bytes(host)
         if item:
             msg = "fatal: [%s] => (item=%s) => %s" % (host, item, results)
         else:
@@ -610,13 +610,13 @@ def on_no_hosts_remaining(self):
         call_callback_module('playbook_on_no_hosts_remaining')
 
     def on_task_start(self, name, is_conditional):
-        name = utils.to_bytes(name)
+        name = utils.unicode.to_bytes(name)
         msg = "TASK: [%s]" % name
         if is_conditional:
             msg = "NOTIFIED: [%s]" % name
 
         if hasattr(self, 'start_at'):
-            self.start_at = utils.to_bytes(self.start_at)
+            self.start_at = utils.unicode.to_bytes(self.start_at)
             if name == self.start_at or fnmatch.fnmatch(name, self.start_at):
                 # we found out match, we can get rid of this now
                 del self.start_at
@@ -630,7 +630,7 @@ def on_task_start(self, name, is_conditional):
             self.skip_task = True
         elif hasattr(self, 'step') and self.step:
             if isinstance(name, str):
-                name = utils.to_unicode(name)
+                name = utils.unicode.to_unicode(name)
             msg = u'Perform task: %s (y/n/c): ' % name
             if sys.stdout.encoding:
                 msg = msg.encode(sys.stdout.encoding, errors='replace')
diff --git a/lib/ansible/utils/__init__.py b/lib/ansible/utils/__init__.py
--- a/lib/ansible/utils/__init__.py
+++ b/lib/ansible/utils/__init__.py
@@ -33,6 +33,7 @@
 from ansible.callbacks import display
 from ansible.module_utils.splitter import split_args, unquote
 from ansible.module_utils.basic import heuristic_log_sanitize
+from ansible.utils.unicode import to_bytes, to_unicode
 import ansible.constants as C
 import ast
 import time
@@ -1088,9 +1089,9 @@ def ask_vault_passwords(ask_vault_pass=False, ask_new_vault_pass=False, confirm_
 
     # enforce no newline chars at the end of passwords
     if vault_pass:
-        vault_pass = vault_pass.strip()
+        vault_pass = to_bytes(vault_pass, errors='strict', nonstring='simplerepr').strip()
     if new_vault_pass:
-        new_vault_pass = new_vault_pass.strip()
+        new_vault_pass = to_bytes(new_vault_pass, errors='strict', nonstring='simplerepr').strip()
 
     return vault_pass, new_vault_pass
 
@@ -1104,20 +1105,31 @@ def ask_passwords(ask_pass=False, ask_sudo_pass=False, ask_su_pass=False, ask_va
 
     if ask_pass:
         sshpass = getpass.getpass(prompt="SSH password: ")
+        if sshpass:
+            sshpass = to_bytes(sshpass, errors='strict', nonstring='simplerepr')
         sudo_prompt = "sudo password [defaults to SSH password]: "
+        su_prompt = "su password [defaults to SSH password]: "
 
     if ask_sudo_pass:
         sudopass = getpass.getpass(prompt=sudo_prompt)
         if ask_pass and sudopass == '':
             sudopass = sshpass
+        if sudopass:
+            sudopass = to_bytes(sudopass, errors='strict', nonstring='simplerepr')
 
     if ask_su_pass:
-        su_pass = getpass.getpass(prompt=su_prompt)
+        supass = getpass.getpass(prompt=su_prompt)
+        if ask_pass and supass == '':
+            supass = sshpass
+        if supass:
+            supass = to_bytes(supass, errors='strict', nonstring='simplerepr')
 
     if ask_vault_pass:
-        vault_pass = getpass.getpass(prompt="Vault password: ")
+        vaultpass = getpass.getpass(prompt="Vault password: ")
+        if vaultpass:
+            vaultpass = to_bytes(vault_pass, errors='strict', nonstring='simplerepr').strip()
 
-    return (sshpass, sudopass, su_pass, vault_pass)
+    return (sshpass, sudopass, supass, vaultpass)
 
 def do_encrypt(result, encrypt, salt_size=None, salt=None):
     if PASSLIB_AVAILABLE:
@@ -1204,25 +1216,6 @@ def make_su_cmd(su_user, executable, cmd):
     )
     return ('/bin/sh -c ' + pipes.quote(sudocmd), None, success_key)
 
-# For v2, consider either using kitchen or copying my code from there for
-# to_unicode and to_bytes handling (TEK)
-_TO_UNICODE_TYPES = (unicode, type(None))
-
-def to_unicode(value):
-    # Use with caution -- this function is not encoding safe (non-utf-8 values
-    # will cause tracebacks if they contain bytes from 0x80-0xff inclusive)
-    if isinstance(value, _TO_UNICODE_TYPES):
-        return value
-    return value.decode("utf-8")
-
-def to_bytes(value):
-    # Note: value is assumed to be a basestring to mirror to_unicode.  Better
-    # implementations (like kitchen.text.converters.to_bytes) bring that check
-    # into the function
-    if isinstance(value, str):
-        return value
-    return value.encode('utf-8')
-
 def get_diff(diff):
     # called by --diff usage in playbook and runner via callbacks
     # include names in diffs 'before' and 'after' and do diff -U 10


Test patch:
diff --git a/test/units/TestUtils.py b/test/units/TestUtils.py
--- a/test/units/TestUtils.py
+++ b/test/units/TestUtils.py
@@ -512,15 +512,15 @@ def test_make_su_cmd(self):
         self.assertTrue('echo SUDO-SUCCESS-' in cmd[0] and cmd[2].startswith('SUDO-SUCCESS-'))
 
     def test_to_unicode(self):
-        uni = ansible.utils.to_unicode(u'ansible')
+        uni = ansible.utils.unicode.to_unicode(u'ansible')
         self.assertTrue(isinstance(uni, unicode))
         self.assertEqual(uni, u'ansible')
 
-        none = ansible.utils.to_unicode(None)
+        none = ansible.utils.unicode.to_unicode(None, nonstring='passthru')
         self.assertTrue(isinstance(none, type(None)))
         self.assertTrue(none is None)
 
-        utf8 = ansible.utils.to_unicode('ansible')
+        utf8 = ansible.utils.unicode.to_unicode('ansible')
         self.assertTrue(isinstance(utf8, unicode))
         self.assertEqual(utf8, u'ansible')