summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/general/plugins/modules/htpasswd.py
diff options
context:
space:
mode:
Diffstat (limited to 'ansible_collections/community/general/plugins/modules/htpasswd.py')
-rw-r--r--ansible_collections/community/general/plugins/modules/htpasswd.py115
1 files changed, 43 insertions, 72 deletions
diff --git a/ansible_collections/community/general/plugins/modules/htpasswd.py b/ansible_collections/community/general/plugins/modules/htpasswd.py
index 180b02073..9633ce2fb 100644
--- a/ansible_collections/community/general/plugins/modules/htpasswd.py
+++ b/ansible_collections/community/general/plugins/modules/htpasswd.py
@@ -26,51 +26,53 @@ options:
required: true
aliases: [ dest, destfile ]
description:
- - Path to the file that contains the usernames and passwords
+ - Path to the file that contains the usernames and passwords.
name:
type: str
required: true
aliases: [ username ]
description:
- - User name to add or remove
+ - User name to add or remove.
password:
type: str
required: false
description:
- Password associated with user.
- Must be specified if user does not exist yet.
- crypt_scheme:
+ hash_scheme:
type: str
required: false
default: "apr_md5_crypt"
description:
- - Encryption scheme to be used. As well as the four choices listed
+ - Hashing scheme to be used. As well as the four choices listed
here, you can also use any other hash supported by passlib, such as
- C(portable_apache22) and C(host_apache24); or C(md5_crypt) and C(sha256_crypt),
- which are Linux passwd hashes. Only some schemes in addition to
+ V(portable_apache22) and V(host_apache24); or V(md5_crypt) and V(sha256_crypt),
+ which are Linux passwd hashes. Only some schemes in addition to
the four choices below will be compatible with Apache or Nginx, and
supported schemes depend on passlib version and its dependencies.
- See U(https://passlib.readthedocs.io/en/stable/lib/passlib.apache.html#passlib.apache.HtpasswdFile) parameter C(default_scheme).
- - 'Some of the available choices might be: C(apr_md5_crypt), C(des_crypt), C(ldap_sha1), C(plaintext).'
+ - 'Some of the available choices might be: V(apr_md5_crypt), V(des_crypt), V(ldap_sha1), V(plaintext).'
+ aliases: [crypt_scheme]
state:
type: str
required: false
choices: [ present, absent ]
default: "present"
description:
- - Whether the user entry should be present or not
+ - Whether the user entry should be present or not.
create:
required: false
type: bool
default: true
description:
- - Used with I(state=present). If specified, the file will be created
- if it does not already exist. If set to C(false), will fail if the
- file does not exist
+ - Used with O(state=present). If V(true), the file will be created
+ if it does not exist. Conversely, if set to V(false) and the file
+ does not exist it will fail.
notes:
- - "This module depends on the I(passlib) Python library, which needs to be installed on all target systems."
- - "On Debian, Ubuntu, or Fedora: install I(python-passlib)."
- - "On RHEL or CentOS: Enable EPEL, then install I(python-passlib)."
+ - "This module depends on the C(passlib) Python library, which needs to be installed on all target systems."
+ - "On Debian < 11, Ubuntu <= 20.04, or Fedora: install C(python-passlib)."
+ - "On Debian, Ubuntu: install C(python3-passlib)."
+ - "On RHEL or CentOS: Enable EPEL, then install C(python-passlib)."
requirements: [ passlib>=1.6 ]
author: "Ansible Core Team"
extends_documentation_fragment:
@@ -99,28 +101,22 @@ EXAMPLES = """
path: /etc/mail/passwords
name: alex
password: oedu2eGh
- crypt_scheme: md5_crypt
+ hash_scheme: md5_crypt
"""
import os
import tempfile
-import traceback
-from ansible.module_utils.basic import AnsibleModule, missing_required_lib
+
+from ansible.module_utils.basic import AnsibleModule
+from ansible_collections.community.general.plugins.module_utils import deps
from ansible.module_utils.common.text.converters import to_native
-from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
-PASSLIB_IMP_ERR = None
-try:
+with deps.declare("passlib"):
from passlib.apache import HtpasswdFile, htpasswd_context
from passlib.context import CryptContext
- import passlib
-except ImportError:
- PASSLIB_IMP_ERR = traceback.format_exc()
- passlib_installed = False
-else:
- passlib_installed = True
+
apache_hashes = ["apr_md5_crypt", "des_crypt", "ldap_sha1", "plaintext"]
@@ -131,50 +127,34 @@ def create_missing_directories(dest):
os.makedirs(destpath)
-def present(dest, username, password, crypt_scheme, create, check_mode):
+def present(dest, username, password, hash_scheme, create, check_mode):
""" Ensures user is present
Returns (msg, changed) """
- if crypt_scheme in apache_hashes:
+ if hash_scheme in apache_hashes:
context = htpasswd_context
else:
- context = CryptContext(schemes=[crypt_scheme] + apache_hashes)
+ context = CryptContext(schemes=[hash_scheme] + apache_hashes)
if not os.path.exists(dest):
if not create:
raise ValueError('Destination %s does not exist' % dest)
if check_mode:
return ("Create %s" % dest, True)
create_missing_directories(dest)
- if LooseVersion(passlib.__version__) >= LooseVersion('1.6'):
- ht = HtpasswdFile(dest, new=True, default_scheme=crypt_scheme, context=context)
- else:
- ht = HtpasswdFile(dest, autoload=False, default=crypt_scheme, context=context)
- if getattr(ht, 'set_password', None):
- ht.set_password(username, password)
- else:
- ht.update(username, password)
+ ht = HtpasswdFile(dest, new=True, default_scheme=hash_scheme, context=context)
+ ht.set_password(username, password)
ht.save()
return ("Created %s and added %s" % (dest, username), True)
else:
- if LooseVersion(passlib.__version__) >= LooseVersion('1.6'):
- ht = HtpasswdFile(dest, new=False, default_scheme=crypt_scheme, context=context)
- else:
- ht = HtpasswdFile(dest, default=crypt_scheme, context=context)
+ ht = HtpasswdFile(dest, new=False, default_scheme=hash_scheme, context=context)
- found = None
- if getattr(ht, 'check_password', None):
- found = ht.check_password(username, password)
- else:
- found = ht.verify(username, password)
+ found = ht.check_password(username, password)
if found:
return ("%s already present" % username, False)
else:
if not check_mode:
- if getattr(ht, 'set_password', None):
- ht.set_password(username, password)
- else:
- ht.update(username, password)
+ ht.set_password(username, password)
ht.save()
return ("Add/update %s" % username, True)
@@ -183,10 +163,7 @@ def absent(dest, username, check_mode):
""" Ensures user is absent
Returns (msg, changed) """
- if LooseVersion(passlib.__version__) >= LooseVersion('1.6'):
- ht = HtpasswdFile(dest, new=False)
- else:
- ht = HtpasswdFile(dest)
+ ht = HtpasswdFile(dest, new=False)
if username not in ht.users():
return ("%s not present" % username, False)
@@ -215,7 +192,7 @@ def main():
path=dict(type='path', required=True, aliases=["dest", "destfile"]),
name=dict(type='str', required=True, aliases=["username"]),
password=dict(type='str', required=False, default=None, no_log=True),
- crypt_scheme=dict(type='str', required=False, default="apr_md5_crypt"),
+ hash_scheme=dict(type='str', required=False, default="apr_md5_crypt", aliases=["crypt_scheme"]),
state=dict(type='str', required=False, default="present", choices=["present", "absent"]),
create=dict(type='bool', default=True),
@@ -227,25 +204,18 @@ def main():
path = module.params['path']
username = module.params['name']
password = module.params['password']
- crypt_scheme = module.params['crypt_scheme']
+ hash_scheme = module.params['hash_scheme']
state = module.params['state']
create = module.params['create']
check_mode = module.check_mode
- if not passlib_installed:
- module.fail_json(msg=missing_required_lib("passlib"), exception=PASSLIB_IMP_ERR)
+ deps.validate(module)
+ # TODO double check if this hack below is still needed.
# Check file for blank lines in effort to avoid "need more than 1 value to unpack" error.
try:
- f = open(path, "r")
- except IOError:
- # No preexisting file to remove blank lines from
- f = None
- else:
- try:
+ with open(path, "r") as f:
lines = f.readlines()
- finally:
- f.close()
# If the file gets edited, it returns true, so only edit the file if it has blank lines
strip = False
@@ -259,15 +229,16 @@ def main():
if check_mode:
temp = tempfile.NamedTemporaryFile()
path = temp.name
- f = open(path, "w")
- try:
- [f.write(line) for line in lines if line.strip()]
- finally:
- f.close()
+ with open(path, "w") as f:
+ f.writelines(line for line in lines if line.strip())
+
+ except IOError:
+ # No preexisting file to remove blank lines from
+ pass
try:
if state == 'present':
- (msg, changed) = present(path, username, password, crypt_scheme, create, check_mode)
+ (msg, changed) = present(path, username, password, hash_scheme, create, check_mode)
elif state == 'absent':
if not os.path.exists(path):
module.exit_json(msg="%s not present" % username,