1
0
Fork 0
mirror of https://github.com/ansible-collections/community.general.git synced 2024-09-14 20:13:21 +02:00
community.general/plugins/modules/htpasswd.py

287 lines
9.2 KiB
Python
Raw Permalink Normal View History

2020-03-09 10:11:07 +01:00
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Copyright (c) 2013, Nimbis Services, Inc.
# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt)
# SPDX-License-Identifier: GPL-3.0-or-later
2020-03-09 10:11:07 +01:00
from __future__ import absolute_import, division, print_function
__metaclass__ = type
DOCUMENTATION = '''
module: htpasswd
short_description: Manage user files for basic authentication
2020-03-09 10:11:07 +01:00
description:
- Add and remove username/password entries in a password file using htpasswd.
- This is used by web servers such as Apache and Nginx for basic authentication.
attributes:
check_mode:
support: full
diff_mode:
support: none
2020-03-09 10:11:07 +01:00
options:
path:
Enabling validation-modules for web_infrastructure modules (#1200) * fixed validation-modules for apache2_mod_proxy.py * fixed validation-modules for apache2_module.py * fixed validation-modules for deploy_helper.py The ignore lines were put back in place because add_file_common_args=True is used and the module inherits a number of options that do not show up in the documentation (nor should they). * fixed validation-modules for ejabberd_user.py * fixed validation-modules for gunicorn.py * fixed validation-modules for htpasswd.py * fixed validation-modules for jenkins_job.py * fixed validation-modules for jenkins_job_info.py * fixed validation-modules for jenkins_plugin.py * fixed validation-modules for jenkins_script.py * fixed validation-modules for jira.py * fixed validation-modules for nginx_status_facts.py * fixed validation-modules for rundeck_acl_policy.py * fixed validation-modules for rundeck_project.py * fixed validation-modules for supervisorctl.py * fixed validation-modules for taiga_issue.py * fixed pylint mistake in plugins/modules/web_infrastructure/jenkins_job_info.py * removed ignore lines for almost-all web_infrastructure modules * rolled back ignore lines for nested sophos_utm modules that were not adjusted * Removed doc-missing-type from ignore-2.11.txt for deploy_helper and jenkins_plugin * When adding lines back to ignore files, we added more than it was before. Removing. * Rolled back deprecation line in ignore-2.9.txt for nginx_status_facts * Rolled back yet another line in ignore-2.9.txt for nginx_status_facts * Fixed argument_spec and docs for crypt_scheme parameter in htpasswd, per PR * Added extends_documentation_fragment:files to deploy_helper and jenkins_plugin * Removed long-deprecated option params from jenkins_plugin, removed validate-modules lines from ignore files for that module * Update plugins/modules/web_infrastructure/htpasswd.py Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
2020-10-31 13:53:57 +01:00
type: path
2020-03-09 10:11:07 +01:00
required: true
aliases: [ dest, destfile ]
description:
- Path to the file that contains the usernames and passwords
name:
Enabling validation-modules for web_infrastructure modules (#1200) * fixed validation-modules for apache2_mod_proxy.py * fixed validation-modules for apache2_module.py * fixed validation-modules for deploy_helper.py The ignore lines were put back in place because add_file_common_args=True is used and the module inherits a number of options that do not show up in the documentation (nor should they). * fixed validation-modules for ejabberd_user.py * fixed validation-modules for gunicorn.py * fixed validation-modules for htpasswd.py * fixed validation-modules for jenkins_job.py * fixed validation-modules for jenkins_job_info.py * fixed validation-modules for jenkins_plugin.py * fixed validation-modules for jenkins_script.py * fixed validation-modules for jira.py * fixed validation-modules for nginx_status_facts.py * fixed validation-modules for rundeck_acl_policy.py * fixed validation-modules for rundeck_project.py * fixed validation-modules for supervisorctl.py * fixed validation-modules for taiga_issue.py * fixed pylint mistake in plugins/modules/web_infrastructure/jenkins_job_info.py * removed ignore lines for almost-all web_infrastructure modules * rolled back ignore lines for nested sophos_utm modules that were not adjusted * Removed doc-missing-type from ignore-2.11.txt for deploy_helper and jenkins_plugin * When adding lines back to ignore files, we added more than it was before. Removing. * Rolled back deprecation line in ignore-2.9.txt for nginx_status_facts * Rolled back yet another line in ignore-2.9.txt for nginx_status_facts * Fixed argument_spec and docs for crypt_scheme parameter in htpasswd, per PR * Added extends_documentation_fragment:files to deploy_helper and jenkins_plugin * Removed long-deprecated option params from jenkins_plugin, removed validate-modules lines from ignore files for that module * Update plugins/modules/web_infrastructure/htpasswd.py Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
2020-10-31 13:53:57 +01:00
type: str
2020-03-09 10:11:07 +01:00
required: true
aliases: [ username ]
description:
- User name to add or remove
password:
Enabling validation-modules for web_infrastructure modules (#1200) * fixed validation-modules for apache2_mod_proxy.py * fixed validation-modules for apache2_module.py * fixed validation-modules for deploy_helper.py The ignore lines were put back in place because add_file_common_args=True is used and the module inherits a number of options that do not show up in the documentation (nor should they). * fixed validation-modules for ejabberd_user.py * fixed validation-modules for gunicorn.py * fixed validation-modules for htpasswd.py * fixed validation-modules for jenkins_job.py * fixed validation-modules for jenkins_job_info.py * fixed validation-modules for jenkins_plugin.py * fixed validation-modules for jenkins_script.py * fixed validation-modules for jira.py * fixed validation-modules for nginx_status_facts.py * fixed validation-modules for rundeck_acl_policy.py * fixed validation-modules for rundeck_project.py * fixed validation-modules for supervisorctl.py * fixed validation-modules for taiga_issue.py * fixed pylint mistake in plugins/modules/web_infrastructure/jenkins_job_info.py * removed ignore lines for almost-all web_infrastructure modules * rolled back ignore lines for nested sophos_utm modules that were not adjusted * Removed doc-missing-type from ignore-2.11.txt for deploy_helper and jenkins_plugin * When adding lines back to ignore files, we added more than it was before. Removing. * Rolled back deprecation line in ignore-2.9.txt for nginx_status_facts * Rolled back yet another line in ignore-2.9.txt for nginx_status_facts * Fixed argument_spec and docs for crypt_scheme parameter in htpasswd, per PR * Added extends_documentation_fragment:files to deploy_helper and jenkins_plugin * Removed long-deprecated option params from jenkins_plugin, removed validate-modules lines from ignore files for that module * Update plugins/modules/web_infrastructure/htpasswd.py Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
2020-10-31 13:53:57 +01:00
type: str
2020-03-09 10:11:07 +01:00
required: false
description:
- Password associated with user.
- Must be specified if user does not exist yet.
crypt_scheme:
Enabling validation-modules for web_infrastructure modules (#1200) * fixed validation-modules for apache2_mod_proxy.py * fixed validation-modules for apache2_module.py * fixed validation-modules for deploy_helper.py The ignore lines were put back in place because add_file_common_args=True is used and the module inherits a number of options that do not show up in the documentation (nor should they). * fixed validation-modules for ejabberd_user.py * fixed validation-modules for gunicorn.py * fixed validation-modules for htpasswd.py * fixed validation-modules for jenkins_job.py * fixed validation-modules for jenkins_job_info.py * fixed validation-modules for jenkins_plugin.py * fixed validation-modules for jenkins_script.py * fixed validation-modules for jira.py * fixed validation-modules for nginx_status_facts.py * fixed validation-modules for rundeck_acl_policy.py * fixed validation-modules for rundeck_project.py * fixed validation-modules for supervisorctl.py * fixed validation-modules for taiga_issue.py * fixed pylint mistake in plugins/modules/web_infrastructure/jenkins_job_info.py * removed ignore lines for almost-all web_infrastructure modules * rolled back ignore lines for nested sophos_utm modules that were not adjusted * Removed doc-missing-type from ignore-2.11.txt for deploy_helper and jenkins_plugin * When adding lines back to ignore files, we added more than it was before. Removing. * Rolled back deprecation line in ignore-2.9.txt for nginx_status_facts * Rolled back yet another line in ignore-2.9.txt for nginx_status_facts * Fixed argument_spec and docs for crypt_scheme parameter in htpasswd, per PR * Added extends_documentation_fragment:files to deploy_helper and jenkins_plugin * Removed long-deprecated option params from jenkins_plugin, removed validate-modules lines from ignore files for that module * Update plugins/modules/web_infrastructure/htpasswd.py Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
2020-10-31 13:53:57 +01:00
type: str
2020-03-09 10:11:07 +01:00
required: false
default: "apr_md5_crypt"
description:
- Encryption 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
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).'
2020-03-09 10:11:07 +01:00
state:
Enabling validation-modules for web_infrastructure modules (#1200) * fixed validation-modules for apache2_mod_proxy.py * fixed validation-modules for apache2_module.py * fixed validation-modules for deploy_helper.py The ignore lines were put back in place because add_file_common_args=True is used and the module inherits a number of options that do not show up in the documentation (nor should they). * fixed validation-modules for ejabberd_user.py * fixed validation-modules for gunicorn.py * fixed validation-modules for htpasswd.py * fixed validation-modules for jenkins_job.py * fixed validation-modules for jenkins_job_info.py * fixed validation-modules for jenkins_plugin.py * fixed validation-modules for jenkins_script.py * fixed validation-modules for jira.py * fixed validation-modules for nginx_status_facts.py * fixed validation-modules for rundeck_acl_policy.py * fixed validation-modules for rundeck_project.py * fixed validation-modules for supervisorctl.py * fixed validation-modules for taiga_issue.py * fixed pylint mistake in plugins/modules/web_infrastructure/jenkins_job_info.py * removed ignore lines for almost-all web_infrastructure modules * rolled back ignore lines for nested sophos_utm modules that were not adjusted * Removed doc-missing-type from ignore-2.11.txt for deploy_helper and jenkins_plugin * When adding lines back to ignore files, we added more than it was before. Removing. * Rolled back deprecation line in ignore-2.9.txt for nginx_status_facts * Rolled back yet another line in ignore-2.9.txt for nginx_status_facts * Fixed argument_spec and docs for crypt_scheme parameter in htpasswd, per PR * Added extends_documentation_fragment:files to deploy_helper and jenkins_plugin * Removed long-deprecated option params from jenkins_plugin, removed validate-modules lines from ignore files for that module * Update plugins/modules/web_infrastructure/htpasswd.py Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
2020-10-31 13:53:57 +01:00
type: str
2020-03-09 10:11:07 +01:00
required: false
choices: [ present, absent ]
default: "present"
description:
- Whether the user entry should be present or not
create:
required: false
type: bool
default: true
2020-03-09 10:11:07 +01:00
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
2020-03-09 10:11:07 +01:00
file does not exist
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)."
requirements: [ passlib>=1.6 ]
author: "Ansible Core Team"
extends_documentation_fragment:
- files
- community.general.attributes
2020-03-09 10:11:07 +01:00
'''
EXAMPLES = """
- name: Add a user to a password file and ensure permissions are set
community.general.htpasswd:
2020-03-09 10:11:07 +01:00
path: /etc/nginx/passwdfile
name: janedoe
password: '9s36?;fyNp'
owner: root
group: www-data
mode: 0640
- name: Remove a user from a password file
community.general.htpasswd:
2020-03-09 10:11:07 +01:00
path: /etc/apache2/passwdfile
name: foobar
state: absent
- name: Add a user to a password file suitable for use by libpam-pwdfile
community.general.htpasswd:
2020-03-09 10:11:07 +01:00
path: /etc/mail/passwords
name: alex
password: oedu2eGh
crypt_scheme: md5_crypt
"""
import os
import tempfile
import traceback
from ansible.module_utils.basic import AnsibleModule, missing_required_lib
from ansible.module_utils.common.text.converters import to_native
2020-03-09 10:11:07 +01:00
from ansible_collections.community.general.plugins.module_utils.version import LooseVersion
2020-03-09 10:11:07 +01:00
PASSLIB_IMP_ERR = None
try:
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"]
def create_missing_directories(dest):
destpath = os.path.dirname(dest)
if not os.path.exists(destpath):
os.makedirs(destpath)
def present(dest, username, password, crypt_scheme, create, check_mode):
""" Ensures user is present
Returns (msg, changed) """
if crypt_scheme in apache_hashes:
context = htpasswd_context
else:
context = CryptContext(schemes=[crypt_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.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)
found = None
if getattr(ht, 'check_password', None):
found = ht.check_password(username, password)
else:
found = ht.verify(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.save()
return ("Add/update %s" % username, True)
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)
if username not in ht.users():
return ("%s not present" % username, False)
else:
if not check_mode:
ht.delete(username)
ht.save()
return ("Remove %s" % username, True)
def check_file_attrs(module, changed, message):
file_args = module.load_file_common_arguments(module.params)
if module.set_fs_attributes_if_different(file_args, False):
if changed:
message += " and "
changed = True
message += "ownership, perms or SE linux context changed"
return message, changed
def main():
arg_spec = dict(
Enabling validation-modules for web_infrastructure modules (#1200) * fixed validation-modules for apache2_mod_proxy.py * fixed validation-modules for apache2_module.py * fixed validation-modules for deploy_helper.py The ignore lines were put back in place because add_file_common_args=True is used and the module inherits a number of options that do not show up in the documentation (nor should they). * fixed validation-modules for ejabberd_user.py * fixed validation-modules for gunicorn.py * fixed validation-modules for htpasswd.py * fixed validation-modules for jenkins_job.py * fixed validation-modules for jenkins_job_info.py * fixed validation-modules for jenkins_plugin.py * fixed validation-modules for jenkins_script.py * fixed validation-modules for jira.py * fixed validation-modules for nginx_status_facts.py * fixed validation-modules for rundeck_acl_policy.py * fixed validation-modules for rundeck_project.py * fixed validation-modules for supervisorctl.py * fixed validation-modules for taiga_issue.py * fixed pylint mistake in plugins/modules/web_infrastructure/jenkins_job_info.py * removed ignore lines for almost-all web_infrastructure modules * rolled back ignore lines for nested sophos_utm modules that were not adjusted * Removed doc-missing-type from ignore-2.11.txt for deploy_helper and jenkins_plugin * When adding lines back to ignore files, we added more than it was before. Removing. * Rolled back deprecation line in ignore-2.9.txt for nginx_status_facts * Rolled back yet another line in ignore-2.9.txt for nginx_status_facts * Fixed argument_spec and docs for crypt_scheme parameter in htpasswd, per PR * Added extends_documentation_fragment:files to deploy_helper and jenkins_plugin * Removed long-deprecated option params from jenkins_plugin, removed validate-modules lines from ignore files for that module * Update plugins/modules/web_infrastructure/htpasswd.py Co-authored-by: Felix Fontein <felix@fontein.de> Co-authored-by: Felix Fontein <felix@fontein.de>
2020-10-31 13:53:57 +01:00
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"),
state=dict(type='str', required=False, default="present", choices=["present", "absent"]),
create=dict(type='bool', default=True),
2020-03-09 10:11:07 +01:00
)
module = AnsibleModule(argument_spec=arg_spec,
add_file_common_args=True,
supports_check_mode=True)
path = module.params['path']
username = module.params['name']
password = module.params['password']
crypt_scheme = module.params['crypt_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)
# 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:
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
for line in lines:
if not line.strip():
strip = True
break
if strip:
# If check mode, create a temporary file
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()
try:
if state == 'present':
(msg, changed) = present(path, username, password, crypt_scheme, create, check_mode)
elif state == 'absent':
if not os.path.exists(path):
module.exit_json(msg="%s not present" % username,
warnings="%s does not exist" % path, changed=False)
(msg, changed) = absent(path, username, check_mode)
else:
module.fail_json(msg="Invalid state: %s" % state)
check_file_attrs(module, changed, msg)
module.exit_json(msg=msg, changed=changed)
except Exception as e:
module.fail_json(msg=to_native(e))
if __name__ == '__main__':
main()