Repository: ansible/ansible

Issue:
Lookup password returns password including the salt - regression from 1.9
<!--- Verify first that your issue/request is not already reported in GitHub -->
##### ISSUE TYPE

<!--- Pick one below and delete the rest: -->
- Bug Report
##### ANSIBLE VERSION

<!--- Paste verbatim output from “ansible --version” between quotes below -->

```
ansible 2.1.0.0
  config file = 
  configured module search path = Default w/o overrides
```
##### CONFIGURATION

<!---
Mention any settings you have changed/added/removed in ansible.cfg
(or using the ANSIBLE_* environment variables).
-->
##### OS / ENVIRONMENT

<!---
Mention the OS you are running Ansible from, and the OS you are
managing, or say “N/A” for anything that is not platform-specific.
-->

N/A
##### SUMMARY

<!--- Explain the problem briefly -->

(Disclaimer: I already [asked on the mailing list](https://groups.google.com/forum/#!msg/ansible-project/TRttzH0jJ54/7P9dKVmXAQAJ) but nobody seemed to take interest in the problem.)

The password produced by a create user task cannot be used somewhere else since `lookup` returns the password including the salt as suffix. Following workflow to create a user with a random password and using the password later as `ansible_sudo_password` worked in 1.9 but is broken in 2.X.

Task for creating the user:

```
- name: Create user
  user: name=deployer group=deployer password={{item}} state=present
  with_password: "passwords/users/deployer encrypt=sha256_crypt"
```

Using the password later on:

```
- hosts: all 
  remote_user: deployer
  vars: 
  - ansible_sudo_pass: "{{ lookup('password', 'passwords/users/deployer') }}"
```



Discussion:

Resolving patch:
diff --git a/lib/ansible/plugins/lookup/password.py b/lib/ansible/plugins/lookup/password.py
--- a/lib/ansible/plugins/lookup/password.py
+++ b/lib/ansible/plugins/lookup/password.py
@@ -137,25 +137,23 @@ def run(self, terms, variables, **kwargs):
 
                 password = content
                 salt = None
-                if params['encrypt'] is not None:
-                    try:
-                        sep = content.rindex(' ')
-                    except ValueError:
-                        # No salt
-                        pass
-                    else:
-                        salt_field = content[sep + 1:]
-                        if salt_field.startswith('salt='):
-                            password = content[:sep]
-                            salt = salt_field[len('salt='):]
 
+                try:
+                    sep = content.rindex(' salt=')
+                except ValueError:
+                    # No salt
+                    pass
+                else:
+                    salt = password[sep + len(' salt='):]
+                    password = content[:sep]
+
+                if params['encrypt'] is not None and salt is None:
                     # crypt requested, add salt if missing
-                    if not salt:
-                        salt = self.random_salt()
-                        content = '%s salt=%s' % (password, salt)
-                        with open(path, 'w') as f:
-                            os.chmod(path, 0o600)
-                            f.write(content + '\n')
+                    salt = self.random_salt()
+                    content = '%s salt=%s' % (password, salt)
+                    with open(path, 'w') as f:
+                        os.chmod(path, 0o600)
+                        f.write(content + '\n')
 
             if params['encrypt']:
                 password = do_encrypt(password, params['encrypt'], salt=salt)


Test patch:
diff --git a/test/integration/roles/test_lookups/tasks/main.yml b/test/integration/roles/test_lookups/tasks/main.yml
--- a/test/integration/roles/test_lookups/tasks/main.yml
+++ b/test/integration/roles/test_lookups/tasks/main.yml
@@ -35,10 +35,11 @@
 
 # PASSWORD LOOKUP
 
-- name: remove previous password files
-  file: dest={{output_dir}}/lookup/password state=absent
+- name: remove previous password files and directory
+  file: dest={{item}} state=absent
   with_items:
   - "{{output_dir}}/lookup/password"
+  - "{{output_dir}}/lookup/password_with_salt"
   - "{{output_dir}}/lookup"
 
 - name: create a password file
@@ -80,6 +81,59 @@
     that:
         - "wc_result.stdout == '9'"
         - "cat_result.stdout == newpass"
+        - "' salt=' not in cat_result.stdout"
+
+- name: fetch password from an existing file
+  set_fact:
+    pass2: "{{ lookup('password', output_dir + '/lookup/password length=8') }}"
+
+- name: read password (again)
+  shell: cat {{output_dir}}/lookup/password
+  register: cat_result2
+
+- debug: var=cat_result2.stdout
+
+- name: verify password (again)
+  assert:
+    that:
+        - "cat_result2.stdout == newpass"
+        - "' salt=' not in cat_result2.stdout"
+
+
+
+- name: create a password (with salt) file
+  debug: msg={{ lookup('password', output_dir + '/lookup/password_with_salt encrypt=sha256_crypt') }}
+
+- name: read password and salt
+  shell: cat {{output_dir}}/lookup/password_with_salt
+  register: cat_pass_salt
+
+- debug: var=cat_pass_salt.stdout
+
+- name: fetch unencrypted password
+  set_fact:
+    newpass: "{{ lookup('password', output_dir + '/lookup/password_with_salt') }}"
+
+- debug: var=newpass
+
+- name: verify password and salt
+  assert:
+    that:
+        - "cat_pass_salt.stdout != newpass"
+        - "cat_pass_salt.stdout.startswith(newpass)"
+        - "' salt=' in cat_pass_salt.stdout"
+        - "' salt=' not in newpass"
+
+
+- name: fetch unencrypted password (using empty encrypt parameter)
+  set_fact:
+    newpass2: "{{ lookup('password', output_dir + '/lookup/password_with_salt encrypt=') }}"
+
+- name: verify lookup password behavior
+  assert:
+    that:
+        - "newpass == newpass2"
+
 
 # ENV LOOKUP