1#!/usr/bin/python
2# -*- coding: utf-8 -*-
3
4# Copyright: (c) 2017, Ansible Project
5# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
6
7from __future__ import absolute_import, division, print_function
8__metaclass__ = type
9
10ANSIBLE_METADATA = {'metadata_version': '1.1',
11                    'status': ['preview'],
12                    'supported_by': 'certified'}
13
14DOCUMENTATION = r'''
15---
16module: cyberark_user
17short_description: Module for CyberArk User Management using PAS Web Services SDK
18author:
19  - Edward Nunez (@enunez-cyberark) CyberArk BizDev
20  - Cyberark Bizdev (@cyberark-bizdev)
21  - erasmix (@erasmix)
22version_added: 2.4
23description:
24    - CyberArk User Management using PAS Web Services SDK.
25    - It currently supports the following actions Get User Details, Add User, Update User, Delete User.
26
27options:
28    username:
29        description:
30            - The name of the user who will be queried (for details), added, updated or deleted.
31        type: str
32        required: True
33    state:
34        description:
35            - Specifies the state needed for the user present for create user, absent for delete user.
36        type: str
37        choices: [ absent, present ]
38        default: present
39    cyberark_session:
40        description:
41            - Dictionary set by a CyberArk authentication containing the different values to perform actions on a logged-on CyberArk session,
42              please see M(cyberark_authentication) module for an example of cyberark_session.
43        type: dict
44        required: True
45    initial_password:
46        description:
47            - The password that the new user will use to log on the first time.
48            - This password must meet the password policy requirements.
49            - This parameter is required when state is present -- Add User.
50        type: str
51    new_password:
52        description:
53            - The user updated password. Make sure that this password meets the password policy requirements.
54        type: str
55    email:
56        description:
57            - The user email address.
58        type: str
59    first_name:
60        description:
61            - The user first name.
62        type: str
63    last_name:
64        description:
65            - The user last name.
66        type: str
67    change_password_on_the_next_logon:
68        description:
69            - Whether or not the user must change their password in their next logon.
70        type: bool
71        default: no
72    expiry_date:
73        description:
74            - The date and time when the user account will expire and become disabled.
75        type: str
76    user_type_name:
77        description:
78            - The type of user.
79            - The parameter defaults to C(EPVUser).
80        type: str
81    disabled:
82        description:
83            - Whether or not the user will be disabled.
84        type: bool
85        default: no
86    location:
87        description:
88            - The Vault Location for the user.
89        type: str
90    group_name:
91        description:
92            - The name of the group the user will be added to.
93        type: str
94'''
95
96EXAMPLES = r'''
97- name: Logon to CyberArk Vault using PAS Web Services SDK
98  cyberark_authentication:
99    api_base_url: https://components.cyberark.local
100    use_shared_logon_authentication: yes
101
102- name: Create user & immediately add it to a group
103  cyberark_user:
104    username: username
105    initial_password: password
106    user_type_name: EPVUser
107    change_password_on_the_next_logon: no
108    group_name: GroupOfUser
109    state: present
110    cyberark_session: '{{ cyberark_session }}'
111
112- name: Make sure user is present and reset user credential if present
113  cyberark_user:
114    username: Username
115    new_password: password
116    disabled: no
117    state: present
118    cyberark_session: '{{ cyberark_session }}'
119
120- name: Logoff from CyberArk Vault
121  cyberark_authentication:
122    state: absent
123    cyberark_session: '{{ cyberark_session }}'
124'''
125
126RETURN = r'''
127changed:
128    description: Whether there was a change done.
129    type: bool
130    returned: always
131cyberark_user:
132    description: Dictionary containing result properties.
133    returned: always
134    type: dict
135    sample:
136        result:
137            description: user properties when state is present
138            type: dict
139            returned: success
140status_code:
141    description: Result HTTP Status code
142    returned: success
143    type: int
144    sample: 200
145'''
146
147import json
148
149from ansible.module_utils.basic import AnsibleModule
150from ansible.module_utils._text import to_text
151from ansible.module_utils.six.moves import http_client as httplib
152from ansible.module_utils.six.moves.urllib.error import HTTPError
153from ansible.module_utils.urls import open_url
154
155
156def user_details(module):
157
158    # Get username from module parameters, and api base url
159    # along with validate_certs from the cyberark_session established
160    username = module.params["username"]
161    cyberark_session = module.params["cyberark_session"]
162    api_base_url = cyberark_session["api_base_url"]
163    validate_certs = cyberark_session["validate_certs"]
164
165    # Prepare result, end_point, and headers
166    result = {}
167    end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{0}".format(
168        username)
169    headers = {'Content-Type': 'application/json'}
170    headers["Authorization"] = cyberark_session["token"]
171
172    try:
173
174        response = open_url(
175            api_base_url + end_point,
176            method="GET",
177            headers=headers,
178            validate_certs=validate_certs)
179        result = {"result": json.loads(response.read())}
180
181        return (False, result, response.getcode())
182
183    except (HTTPError, httplib.HTTPException) as http_exception:
184
185        if http_exception.code == 404:
186            return (False, None, http_exception.code)
187        else:
188            module.fail_json(
189                msg=("Error while performing user_details."
190                     "Please validate parameters provided."
191                     "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, to_text(http_exception))),
192                headers=headers,
193                status_code=http_exception.code)
194
195    except Exception as unknown_exception:
196
197        module.fail_json(
198            msg=("Unknown error while performing user_details."
199                 "\n*** end_point=%s%s\n%s" % (api_base_url, end_point, to_text(unknown_exception))),
200            headers=headers,
201            status_code=-1)
202
203
204def user_add_or_update(module, HTTPMethod):
205
206    # Get username from module parameters, and api base url
207    # along with validate_certs from the cyberark_session established
208    username = module.params["username"]
209    cyberark_session = module.params["cyberark_session"]
210    api_base_url = cyberark_session["api_base_url"]
211    validate_certs = cyberark_session["validate_certs"]
212
213    # Prepare result, payload, and headers
214    result = {}
215    payload = {}
216    headers = {'Content-Type': 'application/json',
217               "Authorization": cyberark_session["token"]}
218
219    # end_point and payload sets different depending on POST/PUT
220    # for POST -- create -- payload contains username
221    # for PUT -- update -- username is part of the endpoint
222    if HTTPMethod == "POST":
223        end_point = "/PasswordVault/WebServices/PIMServices.svc/Users"
224        payload["UserName"] = username
225    elif HTTPMethod == "PUT":
226        end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{0}"
227        end_point = end_point.format(username)
228
229    # --- Optionally populate payload based on parameters passed ---
230    if "initial_password" in module.params:
231        payload["InitialPassword"] = module.params["initial_password"]
232
233    if "new_password" in module.params:
234        payload["NewPassword"] = module.params["new_password"]
235
236    if "email" in module.params:
237        payload["Email"] = module.params["email"]
238
239    if "first_name" in module.params:
240        payload["FirstName"] = module.params["first_name"]
241
242    if "last_name" in module.params:
243        payload["LastName"] = module.params["last_name"]
244
245    if "change_password_on_the_next_logon" in module.params:
246        if module.params["change_password_on_the_next_logon"]:
247            payload["ChangePasswordOnTheNextLogon"] = "true"
248        else:
249            payload["ChangePasswordOnTheNextLogon"] = "false"
250
251    if "expiry_date" in module.params:
252        payload["ExpiryDate"] = module.params["expiry_date"]
253
254    if "user_type_name" in module.params:
255        payload["UserTypeName"] = module.params["user_type_name"]
256
257    if "disabled" in module.params:
258        if module.params["disabled"]:
259            payload["Disabled"] = "true"
260        else:
261            payload["Disabled"] = "false"
262
263    if "location" in module.params:
264        payload["Location"] = module.params["location"]
265    # --------------------------------------------------------------
266
267    try:
268
269        # execute REST action
270        response = open_url(
271            api_base_url + end_point,
272            method=HTTPMethod,
273            headers=headers,
274            data=json.dumps(payload),
275            validate_certs=validate_certs)
276
277        result = {"result": json.loads(response.read())}
278
279        return (True, result, response.getcode())
280
281    except (HTTPError, httplib.HTTPException) as http_exception:
282
283        module.fail_json(
284            msg=("Error while performing user_add_or_update."
285                 "Please validate parameters provided."
286                 "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, to_text(http_exception))),
287            payload=payload,
288            headers=headers,
289            status_code=http_exception.code)
290
291    except Exception as unknown_exception:
292
293        module.fail_json(
294            msg=("Unknown error while performing user_add_or_update."
295                 "\n*** end_point=%s%s\n%s" % (api_base_url, end_point, to_text(unknown_exception))),
296            payload=payload,
297            headers=headers,
298            status_code=-1)
299
300
301def user_delete(module):
302
303    # Get username from module parameters, and api base url
304    # along with validate_certs from the cyberark_session established
305    username = module.params["username"]
306    cyberark_session = module.params["cyberark_session"]
307    api_base_url = cyberark_session["api_base_url"]
308    validate_certs = cyberark_session["validate_certs"]
309
310    # Prepare result, end_point, and headers
311    result = {}
312    end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{0}".format(
313        username)
314
315    headers = {'Content-Type': 'application/json'}
316    headers["Authorization"] = cyberark_session["token"]
317
318    try:
319
320        # execute REST action
321        response = open_url(
322            api_base_url + end_point,
323            method="DELETE",
324            headers=headers,
325            validate_certs=validate_certs)
326
327        result = {"result": {}}
328
329        return (True, result, response.getcode())
330
331    except (HTTPError, httplib.HTTPException) as http_exception:
332
333        exception_text = to_text(http_exception)
334        if http_exception.code == 404 and "ITATS003E" in exception_text:
335            # User does not exist
336            result = {"result": {}}
337            return (False, result, http_exception.code)
338        else:
339            module.fail_json(
340                msg=("Error while performing user_delete."
341                     "Please validate parameters provided."
342                     "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, exception_text)),
343                headers=headers,
344                status_code=http_exception.code)
345
346    except Exception as unknown_exception:
347
348        module.fail_json(
349            msg=("Unknown error while performing user_delete."
350                 "\n*** end_point=%s%s\n%s" % (api_base_url, end_point, to_text(unknown_exception))),
351            headers=headers,
352            status_code=-1)
353
354
355def user_add_to_group(module):
356
357    # Get username, and groupname from module parameters, and api base url
358    # along with validate_certs from the cyberark_session established
359    username = module.params["username"]
360    group_name = module.params["group_name"]
361    cyberark_session = module.params["cyberark_session"]
362    api_base_url = cyberark_session["api_base_url"]
363    validate_certs = cyberark_session["validate_certs"]
364
365    # Prepare result, end_point, headers and payload
366    result = {}
367    end_point = "/PasswordVault/WebServices/PIMServices.svc//Groups/{0}/Users".format(
368        group_name)
369
370    headers = {'Content-Type': 'application/json'}
371    headers["Authorization"] = cyberark_session["token"]
372    payload = {"UserName": username}
373
374    try:
375
376        # execute REST action
377        response = open_url(
378            api_base_url + end_point,
379            method="POST",
380            headers=headers,
381            data=json.dumps(payload),
382            validate_certs=validate_certs)
383
384        result = {"result": {}}
385
386        return (True, result, response.getcode())
387
388    except (HTTPError, httplib.HTTPException) as http_exception:
389
390        exception_text = to_text(http_exception)
391        if http_exception.code == 409 and "ITATS262E" in exception_text:
392            # User is already member of Group
393            return (False, None, http_exception.code)
394        else:
395            module.fail_json(
396                msg=("Error while performing user_add_to_group."
397                     "Please validate parameters provided."
398                     "\n*** end_point=%s%s\n ==> %s" % (api_base_url, end_point, exception_text)),
399                payload=payload,
400                headers=headers,
401                status_code=http_exception.code)
402
403    except Exception as unknown_exception:
404
405        module.fail_json(
406            msg=("Unknown error while performing user_add_to_group."
407                 "\n*** end_point=%s%s\n%s" % (api_base_url, end_point, to_text(unknown_exception))),
408            payload=payload,
409            headers=headers,
410            status_code=-1)
411
412
413def main():
414
415    module = AnsibleModule(
416        argument_spec=dict(
417            username=dict(type='str', required=True),
418            state=dict(type='str', default='present', choices=['absent', 'present']),
419            cyberark_session=dict(type='dict', required=True),
420            initial_password=dict(type='str', no_log=True),
421            new_password=dict(type='str', no_log=True),
422            email=dict(type='str'),
423            first_name=dict(type='str'),
424            last_name=dict(type='str'),
425            change_password_on_the_next_logon=dict(type='bool'),
426            expiry_date=dict(type='str'),
427            user_type_name=dict(type='str'),
428            disabled=dict(type='bool'),
429            location=dict(type='str'),
430            group_name=dict(type='str'),
431        ),
432    )
433
434    state = module.params['state']
435    new_password = module.params['new_password']
436    group_name = module.params['group_name']
437
438    if (state == "present"):
439        (changed, result, status_code) = user_details(module)
440
441        if (status_code == 200):
442            # User already exists
443
444            # If new_password specified, proceed to update user credential
445            if (new_password is not None):
446                (changed, result, status_code) = user_add_or_update(module, "PUT")
447
448            if (group_name is not None):
449                # If user exists, add to group if needed
450                (changed, ignored_result, ignored_status_code) = user_add_to_group(module)
451
452        elif (status_code == 404):
453            # User does not exist, proceed to create it
454            (changed, result, status_code) = user_add_or_update(module, "POST")
455
456            if (status_code == 201 and group_name is not None):
457                # If user was created, add to group if needed
458                (changed, ignored_result, ignored_status_code) = user_add_to_group(module)
459
460    elif (state == "absent"):
461        (changed, result, status_code) = user_delete(module)
462
463    module.exit_json(
464        changed=changed,
465        cyberark_user=result,
466        status_code=status_code)
467
468
469if __name__ == '__main__':
470    main()
471