~ubuntu-branches/ubuntu/raring/ansible/raring-backports

« back to all changes in this revision

Viewing changes to library/web_infrastructure/htpasswd

  • Committer: Package Import Robot
  • Author(s): Felix Geyer
  • Date: 2014-01-10 18:09:14 UTC
  • mfrom: (3.1.5 sid)
  • Revision ID: package-import@ubuntu.com-20140110180914-ywpe153kkue7ho86
Tags: 1.4.4+dfsg-1~ubuntu13.04.1
No-change backport to raring (LP: #1247541)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
# -*- coding: utf-8 -*-
 
3
 
 
4
# (c) 2013, Nimbis Services, Inc.
 
5
#
 
6
# This file is part of Ansible
 
7
#
 
8
# Ansible is free software: you can redistribute it and/or modify
 
9
# it under the terms of the GNU General Public License as published by
 
10
# the Free Software Foundation, either version 3 of the License, or
 
11
# (at your option) any later version.
 
12
#
 
13
# Ansible is distributed in the hope that it will be useful,
 
14
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
# GNU General Public License for more details.
 
17
#
 
18
# You should have received a copy of the GNU General Public License
 
19
# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
 
20
#
 
21
DOCUMENTATION = """
 
22
module: htpasswd
 
23
version_added: "1.3"
 
24
short_description: manage user files for basic authentication
 
25
description:
 
26
  - Add and remove username/password entries in a password file using htpasswd.
 
27
  - This is used by web servers such as Apache and Nginx for basic authentication.
 
28
options:
 
29
  path:
 
30
    required: true
 
31
    aliases: [ dest, destfile ]
 
32
    description:
 
33
      - Path to the file that contains the usernames and passwords
 
34
  name:
 
35
    required: true
 
36
    aliases: [ username ]
 
37
    description:
 
38
      - User name to add or remove
 
39
  password:
 
40
    required: false
 
41
    description:
 
42
      - Password associated with user.
 
43
      - Must be specified if user does not exist yet
 
44
  crypt_scheme:
 
45
    required: false
 
46
    default: "apr_md5_crypt"
 
47
    description:
 
48
      - 'Encryption scheme to be used. One of: "apr_md5_crypt", "des_crypt", "ldap_sha1" or "plaintext"'
 
49
  state:
 
50
    required: false
 
51
    choices: [ present, absent ]
 
52
    default: "present"
 
53
    description:
 
54
      - Whether the user entry should be present or not
 
55
  create:
 
56
    required: false
 
57
    choices: [ "yes", "no" ]
 
58
    default: "yes"
 
59
    description:
 
60
      - Used with C(state=present). If specified, the file will be created
 
61
        if it does not already exist. If set to "no", will fail if the
 
62
        file does not exist
 
63
notes:
 
64
  - "This module depends on the I(passlib) Python library, which needs to be installed on all target systems."
 
65
  - "On Debian, Ubuntu, or Fedora: install I(python-passlib)."
 
66
  - "On RHEL or CentOS: Enable EPEL, then install I(python-passlib)."
 
67
requires: [ passlib>=1.6 ]
 
68
author: Lorin Hochstein
 
69
"""
 
70
 
 
71
EXAMPLES = """
 
72
# Add a user to a password file and ensure permissions are set
 
73
- htpasswd: path=/etc/nginx/passwdfile name=janedoe password=9s36?;fyNp owner=root group=www-data mode=0640
 
74
# Remove a user from a password file
 
75
- htpasswd: path=/etc/apache2/passwdfile name=foobar state=absent
 
76
"""
 
77
 
 
78
 
 
79
import os
 
80
from distutils.version import StrictVersion
 
81
 
 
82
try:
 
83
    from passlib.apache import HtpasswdFile
 
84
    import passlib
 
85
except ImportError:
 
86
    passlib_installed = False
 
87
else:
 
88
    passlib_installed = True
 
89
 
 
90
 
 
91
def create_missing_directories(dest):
 
92
    destpath = os.path.dirname(dest)
 
93
    if not os.path.exists(destpath):
 
94
        os.makedirs(destpath)
 
95
 
 
96
 
 
97
def present(dest, username, password, crypt_scheme, create, check_mode):
 
98
    """ Ensures user is present
 
99
 
 
100
    Returns (msg, changed) """
 
101
    if not os.path.exists(dest):
 
102
        if not create:
 
103
            raise ValueError('Destination %s does not exist' % dest)
 
104
        if check_mode:
 
105
            return ("Create %s" % dest, True)
 
106
        create_missing_directories(dest)
 
107
        if StrictVersion(passlib.__version__) >= StrictVersion('1.6'):
 
108
            ht = HtpasswdFile(dest, new=True, default_scheme=crypt_scheme)
 
109
        else:
 
110
            ht = HtpasswdFile(dest, autoload=False, default=crypt_scheme)
 
111
        if getattr(ht, 'set_password', None):
 
112
            ht.set_password(username, password)
 
113
        else:
 
114
            ht.update(username, password)
 
115
        ht.save()
 
116
        return ("Created %s and added %s" % (dest, username), True)
 
117
    else:
 
118
        if StrictVersion(passlib.__version__) >= StrictVersion('1.6'):
 
119
            ht = HtpasswdFile(dest, new=False, default_scheme=crypt_scheme)
 
120
        else:
 
121
            ht = HtpasswdFile(dest, default=crypt_scheme)
 
122
 
 
123
        found = None
 
124
        if getattr(ht, 'check_password', None):
 
125
            found = ht.check_password(username, password)
 
126
        else:
 
127
            found = ht.verify(username, password)
 
128
 
 
129
        if found:
 
130
            return ("%s already present" % username, False)
 
131
        else:
 
132
            if not check_mode:
 
133
                if getattr(ht, 'set_password', None):
 
134
                    ht.set_password(username, password)
 
135
                else:
 
136
                    ht.update(username, password)
 
137
                ht.save()
 
138
            return ("Add/update %s" % username, True)
 
139
 
 
140
 
 
141
def absent(dest, username, check_mode):
 
142
    """ Ensures user is absent
 
143
 
 
144
    Returns (msg, changed) """
 
145
    if not os.path.exists(dest):
 
146
        raise ValueError("%s does not exists" % dest)
 
147
 
 
148
    if StrictVersion(passlib.__version__) >= StrictVersion('1.6'):
 
149
        ht = HtpasswdFile(dest, new=False)
 
150
    else:
 
151
        ht = HtpasswdFile(dest)
 
152
 
 
153
    if username not in ht.users():
 
154
        return ("%s not present" % username, False)
 
155
    else:
 
156
        if not check_mode:
 
157
            ht.delete(username)
 
158
            ht.save()
 
159
        return ("Remove %s" % username, True)
 
160
 
 
161
 
 
162
def check_file_attrs(module, changed, message):
 
163
 
 
164
    file_args = module.load_file_common_arguments(module.params)
 
165
    if module.set_file_attributes_if_different(file_args, False):
 
166
 
 
167
        if changed:
 
168
            message += " and "
 
169
        changed = True
 
170
        message += "ownership, perms or SE linux context changed"
 
171
 
 
172
    return message, changed
 
173
 
 
174
 
 
175
def main():
 
176
    arg_spec = dict(
 
177
        path=dict(required=True, aliases=["dest", "destfile"]),
 
178
        name=dict(required=True, aliases=["username"]),
 
179
        password=dict(required=False, default=None),
 
180
        crypt_scheme=dict(required=False, default=None),
 
181
        state=dict(required=False, default="present"),
 
182
        create=dict(type='bool', default='yes'),
 
183
 
 
184
    )
 
185
    module = AnsibleModule(argument_spec=arg_spec,
 
186
                           add_file_common_args=True,
 
187
                           supports_check_mode=True)
 
188
 
 
189
    path = module.params['path']
 
190
    username = module.params['name']
 
191
    password = module.params['password']
 
192
    crypt_scheme = module.params['crypt_scheme']
 
193
    state = module.params['state']
 
194
    create = module.params['create']
 
195
    check_mode = module.check_mode
 
196
 
 
197
    if not passlib_installed:
 
198
        module.fail_json(msg="This module requires the passlib Python library")
 
199
 
 
200
    try:
 
201
        if state == 'present':
 
202
            (msg, changed) = present(path, username, password, crypt_scheme, create, check_mode)
 
203
        elif state == 'absent':
 
204
            (msg, changed) = absent(path, username, check_mode)
 
205
        else:
 
206
            module.fail_json(msg="Invalid state: %s" % state)
 
207
 
 
208
        check_file_attrs(module, changed, msg)
 
209
        module.exit_json(msg=msg, changed=changed)
 
210
    except Exception, e:
 
211
        module.fail_json(msg=str(e))
 
212
 
 
213
 
 
214
# this is magic, see lib/ansible/module_common.py
 
215
#<<INCLUDE_ANSIBLE_MODULE_COMMON>>
 
216
 
 
217
if __name__ == '__main__':
 
218
    main()