1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3#
4# Ansible module to manage PaloAltoNetworks Firewall
5# (c) 2016, techbizdev <techbizdev@paloaltonetworks.com>
6#
7# This file is part of Ansible
8#
9# Ansible is free software: you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation, either version 3 of the License, or
12# (at your option) any later version.
13#
14# Ansible is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with Ansible.  If not, see <http://www.gnu.org/licenses/>.
21
22ANSIBLE_METADATA = {'metadata_version': '1.1',
23                    'status': ['deprecated'],
24                    'supported_by': 'community'}
25
26
27DOCUMENTATION = '''
28---
29module: panos_admin
30short_description: Add or modify PAN-OS user accounts password.
31description:
32    - PanOS module that allows changes to the user account passwords by doing
33      API calls to the Firewall using pan-api as the protocol.
34author: "Luigi Mori (@jtschichold), Ivan Bojer (@ivanbojer)"
35version_added: "2.3"
36requirements:
37    - pan-python
38deprecated:
39    alternative: Use U(https://galaxy.ansible.com/PaloAltoNetworks/paloaltonetworks) instead.
40    removed_in: "2.12"
41    why: Consolidating code base.
42options:
43    admin_username:
44        description:
45            - username for admin user
46        default: "admin"
47    admin_password:
48        description:
49            - password for admin user
50        required: true
51    role:
52        description:
53            - role for admin user
54    commit:
55        description:
56            - commit if changed
57        type: bool
58        default: 'yes'
59extends_documentation_fragment: panos
60'''
61
62EXAMPLES = '''
63# Set the password of user admin to "badpassword"
64# Doesn't commit the candidate config
65  - name: set admin password
66    panos_admin:
67      ip_address: "192.168.1.1"
68      password: "admin"
69      admin_username: admin
70      admin_password: "badpassword"
71      commit: False
72'''
73
74RETURN = '''
75status:
76    description: success status
77    returned: success
78    type: str
79    sample: "okey dokey"
80'''
81from ansible.module_utils.basic import AnsibleModule
82
83try:
84    import pan.xapi
85    HAS_LIB = True
86except ImportError:
87    HAS_LIB = False
88
89_ADMIN_XPATH = "/config/mgt-config/users/entry[@name='%s']"
90
91
92def admin_exists(xapi, admin_username):
93    xapi.get(_ADMIN_XPATH % admin_username)
94    e = xapi.element_root.find('.//entry')
95    return e
96
97
98def admin_set(xapi, module, admin_username, admin_password, role):
99    if admin_password is not None:
100        xapi.op(cmd='request password-hash password "%s"' % admin_password,
101                cmd_xml=True)
102        r = xapi.element_root
103        phash = r.find('.//phash').text
104    if role is not None:
105        rbval = "yes"
106        if role != "superuser" and role != 'superreader':
107            rbval = ""
108
109    ea = admin_exists(xapi, admin_username)
110    if ea is not None:
111        # user exists
112        changed = False
113
114        if role is not None:
115            rb = ea.find('.//role-based')
116            if rb is not None:
117                if rb[0].tag != role:
118                    changed = True
119                    xpath = _ADMIN_XPATH % admin_username
120                    xpath += '/permissions/role-based/%s' % rb[0].tag
121                    xapi.delete(xpath=xpath)
122
123                    xpath = _ADMIN_XPATH % admin_username
124                    xpath += '/permissions/role-based'
125                    xapi.set(xpath=xpath,
126                             element='<%s>%s</%s>' % (role, rbval, role))
127
128        if admin_password is not None:
129            xapi.edit(xpath=_ADMIN_XPATH % admin_username + '/phash',
130                      element='<phash>%s</phash>' % phash)
131            changed = True
132
133        return changed
134
135    # setup the non encrypted part of the monitor
136    exml = []
137
138    exml.append('<phash>%s</phash>' % phash)
139    exml.append('<permissions><role-based><%s>%s</%s>'
140                '</role-based></permissions>' % (role, rbval, role))
141
142    exml = ''.join(exml)
143    # module.fail_json(msg=exml)
144
145    xapi.set(xpath=_ADMIN_XPATH % admin_username, element=exml)
146
147    return True
148
149
150def main():
151    argument_spec = dict(
152        ip_address=dict(),
153        password=dict(no_log=True),
154        username=dict(default='admin'),
155        admin_username=dict(default='admin'),
156        admin_password=dict(no_log=True),
157        role=dict(),
158        commit=dict(type='bool', default=True)
159    )
160    module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False)
161
162    if not HAS_LIB:
163        module.fail_json(msg='pan-python required for this module')
164
165    ip_address = module.params["ip_address"]
166    if not ip_address:
167        module.fail_json(msg="ip_address should be specified")
168    password = module.params["password"]
169    if not password:
170        module.fail_json(msg="password is required")
171    username = module.params['username']
172
173    xapi = pan.xapi.PanXapi(
174        hostname=ip_address,
175        api_username=username,
176        api_password=password
177    )
178
179    admin_username = module.params['admin_username']
180    if admin_username is None:
181        module.fail_json(msg="admin_username is required")
182    admin_password = module.params['admin_password']
183    role = module.params['role']
184    commit = module.params['commit']
185
186    changed = admin_set(xapi, module, admin_username, admin_password, role)
187
188    if changed and commit:
189        xapi.commit(cmd="<commit></commit>", sync=True, interval=1)
190
191    module.exit_json(changed=changed, msg="okey dokey")
192
193
194if __name__ == '__main__':
195    main()
196