diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-14 20:03:01 +0000 |
commit | a453ac31f3428614cceb99027f8efbdb9258a40b (patch) | |
tree | f61f87408f32a8511cbd91799f9cececb53e0374 /collections-debian-merged/ansible_collections/cyberark/pas/plugins | |
parent | Initial commit. (diff) | |
download | ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.tar.xz ansible-a453ac31f3428614cceb99027f8efbdb9258a40b.zip |
Adding upstream version 2.10.7+merged+base+2.10.8+dfsg.upstream/2.10.7+merged+base+2.10.8+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'collections-debian-merged/ansible_collections/cyberark/pas/plugins')
6 files changed, 2704 insertions, 0 deletions
diff --git a/collections-debian-merged/ansible_collections/cyberark/pas/plugins/.DS_Store b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/.DS_Store Binary files differnew file mode 100644 index 00000000..22a721d7 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/.DS_Store diff --git a/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/.DS_Store b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/.DS_Store Binary files differnew file mode 100644 index 00000000..5008ddfc --- /dev/null +++ b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/.DS_Store diff --git a/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_account.py b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_account.py new file mode 100644 index 00000000..65a3499a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_account.py @@ -0,0 +1,1378 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: cyberark_account +short_description: Module for CyberArk Account object creation, deletion, and + modification using PAS Web Services SDK. +author: + - CyberArk BizDev (@cyberark-bizdev) + - Edward Nunez (@enunez-cyberark) + - James Stutes (@jimmyjamcabd) +version_added: 2.4 +description: + - Creates a URI for adding, deleting, modifying a privileged credential + within the Cyberark Vault. The request uses the Privileged Account + Security Web Services SDK. + + +options: + state: + description: + - Assert the desired state of the account C(present) to creat or + update and account object. Set to C(absent) for deletion of an + account object. + required: true + default: present + choices: [present, absent] + type: str + logging_level: + description: + - Parameter used to define the level of troubleshooting output to + the C(logging_file) value. + required: true + choices: [NOTSET, DEBUG, INFO] + type: str + logging_file: + description: + - Setting the log file name and location for troubleshooting logs. + required: false + default: /tmp/ansible_cyberark.log + type: str + api_base_url: + description: + - A string containing the base URL of the server hosting CyberArk's + Privileged Account Security Web Services SDK. + - Example U(https://<IIS_Server_Ip>/PasswordVault/api/) + required: true + type: str + validate_certs: + description: + - If C(false), SSL certificate chain will not be validated. This + should only set to C(true) if you have a root CA certificate + installed on each node. + required: false + default: true + type: bool + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the + different values to perform actions on a logged-on CyberArk + session, please see M(cyberark_authentication) module for an + example of cyberark_session. + required: true + type: dict + identified_by: + description: + - When an API call is made to Get Accounts, often times the default + parameters passed will identify more than one account. This + parameter is used to confidently identify a single account when + the default query can return multiple results. + required: false + default: username,address,platform_id + type: str + safe: + description: + - The safe in the Vault where the privileged account is to be + located. + required: true + type: str + platform_id: + description: + - The PolicyID of the Platform that is to be managing the account + required: false + type: str + address: + description: + - The address of the endpoint where the privileged account is + located. + required: false + type: str + name: + description: + - The ObjectID of the account + required: false + type: str + secret_type: + description: + - The value that identifies what type of account it will be. + required: false + default: password + choices: [password, key] + type: str + secret: + description: + - The initial password for the creation of the account + required: false + type: str + new_secret: + description: + - The new secret/password to be stored in CyberArk Vault. + type: str + username: + description: + - The username associated with the account. + required: false + type: str + secret_management: + description: + - Set of parameters associated with the management of the + credential. + required: false + type: dict + suboptions: + automatic_management_enabled: + description: + - Parameter that indicates whether the CPM will manage + the password or not. + default: False + type: bool + manual_management_reason: + description: + - String value indicating why the CPM will NOT manage + the password. + type: str + management_action: + description: + - CPM action flag to be placed on the account object + for credential rotation. + choices: [change, change_immediately, reconcile] + type: str + new_secret: + description: + - The actual password value that will be assigned for + the CPM action to be taken. + type: str + perform_management_action: + description: + - C(always) will perform the management action in + every action. + - C(on_create) will only perform the management action + right after the account is created. + choices: [always, on_create] + default: always + type: str + remote_machines_access: + description: + - Set of parameters for defining PSM endpoint access targets. + required: false + type: dict + suboptions: + remote_machines: + description: + - List of targets allowed for this account. + type: str + access_restricted_to_remote_machines: + description: + - Whether or not to restrict access only to specified + remote machines. + type: bool + platform_account_properties: + description: + - Object containing key-value pairs to associate with the account, + as defined by the account platform. These properties are + validated against the mandatory and optional properties of the + specified platform's definition. Optional properties that do not + exist on the account will not be returned here. Internal + properties are not returned. + required: false + type: dict + suboptions: + KEY: + description: + - Freeform key value associated to the mandatory or + optional property assigned to the specified + Platform's definition. + aliases: [Port, ExtrPass1Name, database] + type: str +""" + +EXAMPLES = """ + collections: + - cyberark.pas + + tasks: + + - name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: "http://components.cyberark.local" + validate_certs: no + username: "bizdev" + password: "Cyberark1" + + - name: Creating an Account using the PAS WebServices SDK + cyberark_account: + logging_level: DEBUG + identified_by: "address,username" + safe: "Test" + address: "cyberark.local" + username: "administrator-x" + platform_id: WinServerLocal + secret: "@N&Ibl3!" + platform_account_properties: + LogonDomain: "cyberark" + OwnerName: "ansible_user" + secret_management: + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: cyberarkaction + + - name: + - Rotate credential via reconcile and providing the password to + bechanged to. + cyberark_account: + identified_by: "address,username" + safe: "Domain_Admins" + address: "prod.cyberark.local" + username: "admin" + platform_id: WinDomain + platform_account_properties: + LogonDomain: "PROD" + secret_management: + new_secret: "Ama123ah12@#!Xaamdjbdkl@#112" + management_action: "reconcile" + automatic_management_enabled: true + state: present + cyberark_session: "{{ cyberark_session }}" + register: reconcileaccount + + - name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" + +""" +RETURN = """ +changed: + description: + - Identify if the playbook run resulted in a change to the account in + any way. + returned: always + type: bool +failed: + description: Whether playbook run resulted in a failure of any kind. + returned: always + type: bool +status_code: + description: Result HTTP Status code. + returned: success + type: int + sample: "200, 201, -1, 204" +result: + description: A json dump of the resulting action. + returned: success + type: complex + contains: + address: + description: + - The adress of the endpoint where the privileged account is + located. + returned: successful addition and modification + type: str + sample: dev.local + createdTime: + description: + - Timeframe calculation of the timestamp of account creation. + returned: successful addition and modification + type: int + sample: "1567824520" + id: + description: Internal ObjectID for the account object identified + returned: successful addition and modification + type: int + sample: "25_21" + name: + description: The external ObjectID of the account + returned: successful addition and modification + type: str + sample: + - Operating System-WinServerLocal-cyberark.local-administrator + platformAccountProperties: + description: + - Object containing key-value pairs to associate with the + account, as defined by the account platform. + returned: successful addition and modification + type: complex + contains: + KEY VALUE: + description: + - Object containing key-value pairs to associate with the + account, as defined by the account platform. + returned: successful addition and modification + type: str + sample: + - "LogonDomain": "cyberark" + - "Port": "22" + platformId: + description: + - The PolicyID of the Platform that is to be managing the + account. + returned: successful addition and modification + type: str + sample: WinServerLocal + safeName: + description: + - The safe in the Vault where the privileged account is to + be located. + returned: successful addition and modification + type: str + sample: Domain_Admins + secretManagement: + description: + - Set of parameters associated with the management of + the credential. + returned: successful addition and modification + type: complex + sample: + automaticManagementEnabled: + description: + - Parameter that indicates whether the CPM will manage + the password or not. + returned: successful addition and modification + type: bool + lastModifiedTime: + description: + - Timeframe calculation of the timestamp of account + modification. + returned: successful addition and modification + type: int + sample: "1567824520" + manualManagementReason: + description: + returned: if C(automaticManagementEnabled) is set to false + type: str + sample: This is a static account + secretType: + description: + - The value that identifies what type of account it will be + returned: successful addition and modification + type: list + sample: + - key + - password + userName: + description: The username associated with the account + returned: successful addition and modification + type: str + sample: administrator +""" + + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.error import HTTPError +import urllib + +import json +try: + import httplib +except ImportError: + # Python 3 + import http.client as httplib +import logging + +_empty = object() + +ansible_specific_parameters = [ + "state", + "api_base_url", + "validate_certs", + "cyberark_session", + "identified_by", + "logging_level", + "logging_file", + "new_secret", + "secret_management.management_action", + "secret_management.new_secret", + "management_action", + "secret_management.perform_management_action", +] + +cyberark_fixed_properties = [ + "createdTime", + "id", + "name", + "lastModifiedTime", + "safeName", + "secretType", + "secret", +] + +removal_value = "NO_VALUE" + +cyberark_reference_fieldnames = { + "username": "userName", + "safe": "safeName", + "platform_id": "platformId", + "secret_type": "secretType", + "platform_account_properties": "platformAccountProperties", + "secret_management": "secretManagement", + "manual_management_reason": "manualManagementReason", + "automatic_management_enabled": "automaticManagementEnabled", + "remote_machines_access": "remoteMachinesAccess", + "access_restricted_to_remote_machines": "accessRestrictedToRemoteMachines", + "remote_machines": "remoteMachines", +} + +ansible_reference_fieldnames = { + "userName": "username", + "safeName": "safe", + "platformId": "platform_id", + "secretType": "secret_type", + "platformAccountProperties": "platform_account_properties", + "secretManagement": "secret_management", + "manualManagementReason": "manual_management_reason", + "automaticManagementEnabled": "automatic_management_enabled", + "remoteMachinesAccess": "remote_machines_access", + "accessRestrictedToRemoteMachines": "access_testricted_to_remoteMachines", + "remoteMachines": "remote_machines", +} + + +def equal_value(existing, parameter): + if isinstance(existing, str): + return existing == str(parameter) + elif isinstance(parameter, str): + return str(existing) == parameter + else: + return existing == parameter + + +def update_account(module, existing_account): + + logging.debug("Updating Account") + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {"result": existing_account} + changed = False + last_status_code = -1 + + HTTPMethod = "PATCH" + end_point = "/PasswordVault/api/Accounts/%s" % existing_account["id"] + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + } + + payload = {"Operations": []} + + # Determining whether to add or update properties + for parameter_name in module.params.keys(): + if ( + parameter_name not in ansible_specific_parameters + and module.params[parameter_name] is not None + ): + module_parm_value = module.params[parameter_name] + cyberark_property_name = referenced_value( + parameter_name, + cyberark_reference_fieldnames, + default=parameter_name + ) + existing_account_value = referenced_value( + cyberark_property_name, + existing_account, + keys=existing_account.keys() + ) + if cyberark_property_name not in cyberark_fixed_properties: + if module_parm_value is not None and isinstance( + module_parm_value, dict + ): + # Internal child values + replacing = {} + adding = {} + removing = {} + for child_parm_name in module_parm_value.keys(): + nested_parm_name = "%s.%s" % ( + parameter_name, + child_parm_name) + if ( + nested_parm_name not in ansible_specific_parameters + ): + child_module_parm_value = module_parm_value[ + child_parm_name + ] + child_cyberark_property_name = referenced_value( + child_parm_name, + cyberark_reference_fieldnames, + default=child_parm_name, + ) + child_existing_account_value = referenced_value( + child_cyberark_property_name, + existing_account_value, + existing_account_value.keys() + if existing_account_value is not None + else {}, + ) + path_value = "/%s/%s" % ( + cyberark_property_name, + child_cyberark_property_name, + ) + if child_existing_account_value is not None: + logging.debug( + ("child_module_parm_value: %s " + "child_existing_account_value=%s path=%s") + ( + child_module_parm_value, + child_existing_account_value, + path_value, + ) + ) + if child_module_parm_value == removal_value: + removing.update( + { + child_cyberark_property_name: + child_existing_account_value + } + ) + elif ( + child_module_parm_value is not None + and not equal_value( + child_existing_account_value, + child_module_parm_value, + ) + ): + # Updating a property + replacing.update( + { + child_cyberark_property_name: + child_module_parm_value + } + ) + elif ( + child_module_parm_value is not None + and child_module_parm_value != removal_value + ): + # Adding a property value + adding.update( + { + child_cyberark_property_name: + child_module_parm_value + } + ) + logging.debug( + "parameter_name=%s value=%s existing=%s" + ( + path_value, + child_module_parm_value, + child_existing_account_value, + ) + ) + # Processing child operations + if len(adding.keys()) > 0: + payload["Operations"].append( + { + "op": "add", + "path": "/%s" % cyberark_property_name, + "value": adding, + } + ) + if len(replacing.keys()) > 0: + payload["Operations"].append( + { + "op": "replace", + "path": "/%s" % cyberark_property_name, + "value": replacing, + } + ) + if len(removing) > 0: + payload["Operations"].append( + { + "op": "remove", + "path": "/%s" % cyberark_property_name, + "value": removing, + } + ) + else: + if existing_account_value is not None: + if module_parm_value == removal_value: + payload["Operations"].append( + {"op": "remove", "path": "/%s" % + cyberark_property_name} + ) + elif not equal_value( + existing_account_value, + module_parm_value + ): + # Updating a property + payload["Operations"].append( + { + "op": "replace", + "value": module_parm_value, + "path": "/%s" % cyberark_property_name, + } + ) + elif module_parm_value != removal_value: + # Adding a property value + payload["Operations"].append( + { + "op": "add", + "value": module_parm_value, + "path": "/%s" % cyberark_property_name, + } + ) + logging.debug( + "parameter_name=%s value=%s existing=%s" + ( + parameter_name, module_parm_value, + existing_account_value + ) + ) + + if len(payload["Operations"]) != 0: + if module.check_mode: + logging.debug("Proceeding with Update Account (CHECK_MODE)") + logging.debug("Operations => %s", json.dumps(payload)) + result = {"result": existing_account} + changed = True + last_status_code = -1 + else: + logging.debug("Proceeding with Update Account") + + logging.debug( + "Processing invidual operations (%d) => %s", + len(payload["Operations"]), + json.dumps(payload) + ) + for operation in payload["Operations"]: + individual_payload = [operation] + try: + logging.debug(" ==> %s", json.dumps([operation])) + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(individual_payload), + validate_certs=validate_certs, + ) + + result = {"result": json.loads(response.read())} + changed = True + last_status_code = response.getcode() + + # return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing update_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, res) + ), + payload=individual_payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing update_account." + "\n*** end_point=%s%s\n%s" + % ( + api_base_url, end_point, + to_text(unknown_exception) + ) + ), + payload=individual_payload, + headers=headers, + status_code=-1, + ) + + return (changed, result, last_status_code) + + +def add_account(module): + + logging.debug("Adding Account") + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + HTTPMethod = "POST" + end_point = "/PasswordVault/api/Accounts" + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + } + + payload = {"safeName": module.params["safe"]} + + for parameter_name in module.params.keys(): + if ( + parameter_name not in ansible_specific_parameters + and module.params[parameter_name] is not None + ): + cyberark_property_name = referenced_value( + parameter_name, + cyberark_reference_fieldnames, + default=parameter_name + ) + if isinstance(module.params[parameter_name], dict): + payload[cyberark_property_name] = {} + for dict_key in module.params[parameter_name].keys(): + cyberark_child_property_name = referenced_value( + dict_key, + cyberark_reference_fieldnames, + default=dict_key + ) + logging.debug( + ("parameter_name =%s.%s cyberark_property_name=%s " + "cyberark_child_property_name=%s"), + parameter_name, + dict_key, + cyberark_property_name, + cyberark_child_property_name, + ) + if ( + parameter_name + "." + dict_key + not in ansible_specific_parameters + and module.params[parameter_name][dict_key] is not None + ): + payload[cyberark_property_name][ + cyberark_child_property_name + ] = deep_get( + module.params[parameter_name], + dict_key, + _empty, + False + ) + else: + if parameter_name not in cyberark_reference_fieldnames: + module_parm_value = deep_get( + module.params, parameter_name, _empty, False + ) + if ( + module_parm_value is not None + and module_parm_value != removal_value + ): + payload[ + parameter_name + ] = module_parm_value # module.params[parameter_name] + else: + module_parm_value = deep_get( + module.params, parameter_name, _empty, True + ) + if ( + module_parm_value is not None + and module_parm_value != removal_value + ): + payload[ + cyberark_reference_fieldnames[parameter_name] + ] = module_parm_value # module.params[parameter_name] + logging.debug("parameter_name =%s", parameter_name) + + logging.debug("Add Account Payload => %s", json.dumps(payload)) + + try: + + if module.check_mode: + logging.debug("Proceeding with Add Account (CHECK_MODE)") + return (True, {"result": None}, -1) + else: + logging.debug("Proceeding with Add Account") + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + ) + + result = {"result": json.loads(response.read())} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing add_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" % ( + api_base_url, + end_point, + res + ) + ), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing add_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + + +def delete_account(module, existing_account): + + if module.check_mode: + logging.debug("Deleting Account (CHECK_MODE)") + return (True, {"result": None}, -1) + else: + logging.debug("Deleting Account") + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + HTTPMethod = "DELETE" + end_point = "/PasswordVault/api/Accounts/%s" % existing_account["id"] + + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + } + + try: + + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + validate_certs=validate_certs, + ) + + result = {"result": None} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing delete_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" % ( + api_base_url, + end_point, + res + ) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing delete_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def reset_account_if_needed(module, existing_account): + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Credential changes + management_action = deep_get( + module.params, + "secret_management.management_action", + "NOT_FOUND", + False + ) + cpm_new_secret = deep_get( + module.params, "secret_management.new_secret", "NOT_FOUND", False + ) + logging.debug( + "management_action: %s cpm_new_secret: %s", + management_action, + cpm_new_secret + ) + + # Prepare result, end_point, and headers + result = {} + end_point = None + payload = {} + existing_account_id = None + if existing_account is not None: + existing_account_id = existing_account["id"] + elif module.check_mode: + existing_account_id = 9999 + + if ( + management_action == "change" + and cpm_new_secret is not None + and cpm_new_secret != "NOT_FOUND" + ): + logging.debug("CPM change secret for next CPM cycle") + end_point = ( + "/PasswordVault/API/Accounts/%s/SetNextPassword" + ) % existing_account_id + payload["ChangeImmediately"] = False + payload["NewCredentials"] = cpm_new_secret + elif management_action == "change_immediately" and ( + cpm_new_secret == "NOT_FOUND" or cpm_new_secret is None + ): + logging.debug("CPM change_immediately with random secret") + end_point = ( + "/PasswordVault/API/Accounts/%s/Change" + ) % existing_account_id + payload["ChangeEntireGroup"] = True + elif management_action == "change_immediately" and ( + cpm_new_secret is not None and cpm_new_secret != "NOT_FOUND" + ): + logging.debug("CPM change immediately secret for next CPM cycle") + end_point = ( + "/PasswordVault/API/Accounts/%s/SetNextPassword" + ) % existing_account_id + payload["ChangeImmediately"] = True + payload["NewCredentials"] = cpm_new_secret + elif management_action == "reconcile": + logging.debug("CPM reconcile secret") + end_point = ( + "/PasswordVault/API/Accounts/%s/Reconcile" + ) % existing_account_id + elif ( + "new_secret" in module.params.keys() + and module.params["new_secret"] is not None + ): + logging.debug("Change Credential in Vault") + end_point = ( + "/PasswordVault/API/Accounts/%s/Password/Update" + ) % existing_account_id + payload["ChangeEntireGroup"] = True + payload["NewCredentials"] = module.params["new_secret"] + + if end_point is not None: + + if module.check_mode: + logging.debug("Proceeding with Credential Rotation (CHECK_MODE)") + return (True, result, -1) + else: + logging.debug("Proceeding with Credential Rotation") + + result = {"result": None} + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + } + HTTPMethod = "POST" + try: + + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + ) + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if isinstance(http_exception, HTTPError): + res = json.load(http_exception) + else: + res = to_text(http_exception) + + module.fail_json( + msg=( + "Error while performing reset_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + ) % (api_base_url, end_point, res), + headers=headers, + payload=payload, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing delete_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + payload=payload, + status_code=-1, + ) + + else: + return (False, result, -1) + + +def referenced_value(field, dct, keys=None, default=None): + return dct[field] if field in ( + keys if keys is not None else dct + ) else default + + +def deep_get(dct, dotted_path, default=_empty, use_reference_table=True): + result_dct = {} + for key in dotted_path.split("."): + try: + key_field = key + if use_reference_table: + key_field = referenced_value( + key, cyberark_reference_fieldnames, default=key + ) + + if len(result_dct.keys()) == 0: # No result_dct set yet + result_dct = dct + + logging.debug( + "keys=%s key_field=>%s key=>%s", + ",".join(result_dct.keys()), + key_field, + key + ) + result_dct = ( + result_dct[key_field] + if key_field in result_dct.keys() + else result_dct[key] + ) + if result_dct is None: + return default + + except KeyError as e: + logging.debug("KeyError %s", to_text(e)) + if default is _empty: + raise + return default + return result_dct + + +def get_account(module): + + logging.debug("Finding Account") + + identified_by_fields = module.params["identified_by"].split(",") + logging.debug("Identified_by: %s", json.dumps(identified_by_fields)) + safe_filter = ( + urllib.quote("safeName eq ") + urllib.quote(module.params["safe"]) + if "safe" in module.params and module.params["safe"] is not None + else None + ) + search_string = None + for field in identified_by_fields: + if field not in ansible_specific_parameters: + search_string = "%s%s" % ( + search_string + " " if search_string is not None else "", + deep_get(module.params, field, "NOT FOUND", False), + ) + + logging.debug("Search_String => %s", search_string) + logging.debug("Safe Filter => %s", safe_filter) + + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + end_point = None + if search_string is not None and safe_filter is not None: + end_point = "/PasswordVault/api/accounts?filter=%s&search=%s" % ( + safe_filter, + urllib.quote(search_string.lstrip()), + ) + elif search_string is not None: + end_point = ( + "/PasswordVault/api/accounts?search=%s" + ) % (search_string.lstrip()) + else: + end_point = "/PasswordVault/api/accounts?filter=%s" % (safe_filter) + + logging.debug("End Point => %s", end_point) + + headers = {"Content-Type": "application/json"} + headers["Authorization"] = cyberark_session["token"] + + try: + + logging.debug("Executing: " + api_base_url + end_point) + response = open_url( + api_base_url + end_point, + method="GET", + headers=headers, + validate_certs=validate_certs, + ) + + result_string = response.read() + accounts_data = json.loads(result_string) + + logging.debug("RESULT => %s", json.dumps(accounts_data)) + + if accounts_data["count"] == 0: + return (False, None, response.getcode()) + else: + how_many = 0 + first_record_found = None + for account_record in accounts_data["value"]: + logging.debug("Acct Record => %s", json.dumps(account_record)) + found = False + for field in identified_by_fields: + record_field_value = deep_get( + account_record, + field, + "NOT FOUND" + ) + logging.debug( + ( + "Comparing field %s | record_field_name=%s " + "record_field_value=%s module.params_value=%s" + ), + field, + field, + record_field_value, + deep_get(module.params, field, "NOT FOUND") + ) + if ( + record_field_value != "NOT FOUND" + and ( + record_field_value + == deep_get( + module.params, + field, + "NOT FOUND", + False + ) + ) + ): + found = True + else: + found = False + break + if found: + how_many = how_many + 1 + if first_record_found is None: + first_record_found = account_record + + logging.debug( + "How Many: %d First Record Found => %s", + how_many, + json.dumps(first_record_found) + ) + if how_many > 1: # too many records found + module.fail_json( + msg=( + "Error while performing get_account. " + "Too many rows (%d) found matching your criteria!" + ) % how_many + ) + else: + return (how_many == 1, first_record_found, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if http_exception.code == 404: + return (False, None, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing get_account." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, to_text(http_exception)) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing get_account." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def main(): + + fields = { + "state": { + "type": "str", + "choices": ["present", "absent"], + "default": "present", + }, + "logging_level": { + "type": "str", + "choices": ["NOTSET", "DEBUG", "INFO"] + }, + "logging_file": { + "type": "str", + "default": "/tmp/ansible_cyberark.log" + }, + "api_base_url": {"type": "str"}, + "validate_certs": {"type": "bool", "default": "true"}, + "cyberark_session": {"required": True, "type": "dict", "no_log": True}, + "identified_by": { + "required": False, + "type": "str", + "default": "username,address,platform_id", + }, + "safe": {"required": True, "type": "str"}, + "platform_id": {"required": False, "type": "str"}, + "address": {"required": False, "type": "str"}, + "name": {"required": False, "type": "str"}, + "secret_type": { + "required": False, + "type": "str", + "choices": ["password", "key"], + "default": "password", + }, + "secret": {"required": False, "type": "str", "no_log": True}, + "new_secret": {"required": False, "type": "str", "no_log": True}, + "username": {"required": False, "type": "str"}, + "secret_management": { + "required": False, + "type": "dict", + "options": { + "automatic_management_enabled": {"type": "bool"}, + "manual_management_reason": {"type": "str"}, + "management_action": { + "type": "str", + "choices": ["change", "change_immediately", "reconcile"], + }, + "new_secret": {"type": "str", "no_log": True}, + "perform_management_action": { + "type": "str", + "choices": ["on_create", "always"], + "default": "always", + }, + }, + }, + "remote_machines_access": { + "required": False, + "type": "dict", + "options": { + "remote_machines": {"type": "str"}, + "access_restricted_to_remote_machines": {"type": "bool"}, + }, + }, + "platform_account_properties": {"required": False, "type": "dict"}, + } + + module = AnsibleModule(argument_spec=fields, supports_check_mode=True) + + if module.params["logging_level"] is not None: + logging.basicConfig( + filename=module.params["logging_file"], + level=module.params["logging_level"] + ) + + logging.info("Starting Module") + + state = module.params["state"] + + (found, account_record, status_code) = get_account(module) + logging.debug( + "Account was %s, status_code=%s", + "FOUND" if found else "NOT FOUND", + status_code + ) + + changed = False + result = {"result": account_record} + + if state == "present": + + if found: # Account already exists + (changed, result, status_code) = update_account( + module, + account_record + ) + else: # Account does not exist + (changed, result, status_code) = add_account(module) + + perform_management_action = "always" + if "secret_management" in module.params.keys(): + secret_management = module.params["secret_management"] + if ( + secret_management is not None + and "perform_management_action" in secret_management.keys() + ): + perform_management_action = secret_management[ + "perform_management_action" + ] + + logging.debug("Result=>%s", json.dumps(result)) + if ( + perform_management_action == "always" + or ( + perform_management_action == "on_create" and not found + ) + ): + (account_reset, no_result, no_status_code) = reset_account_if_needed( + module, + result["result"] + ) + if account_reset: + changed = True + + elif found and state == "absent": + (changed, result, status_code) = delete_account(module, account_record) + + module.exit_json(changed=changed, result=result, status_code=status_code) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_authentication.py b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_authentication.py new file mode 100644 index 00000000..324abf65 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_authentication.py @@ -0,0 +1,357 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "certified", +} + +DOCUMENTATION = ''' +--- +module: cyberark_authentication +short_description: CyberArk Authentication using PAS Web Services SDK. +author: + - Edward Nunez (@enunez-cyberark) CyberArk BizDev + - Cyberark Bizdev (@cyberark-bizdev) + - Erasmo Acosta (@erasmix) +version_added: 2.4 +description: + - Authenticates to CyberArk Vault using Privileged Account Security + Web Services SDK and creates a session fact that can be used by other + modules. It returns an Ansible fact called I(cyberark_session). Every + module can use this fact as C(cyberark_session) parameter. +options: + state: + default: present + choices: [present, absent] + description: + - Specifies if an authentication logon/logoff and a + cyberark_session should be added/removed. + type: str + username: + description: + - The name of the user who will logon to the Vault. + type: str + password: + description: + - The password of the user. + type: str + new_password: + description: + - The new password of the user. This parameter is optional, + and enables you to change a password. + type: str + api_base_url: + description: + - A string containing the base URL of the server hosting + CyberArk's Privileged Account Security Web Services SDK. + type: str + validate_certs: + type: bool + default: 'yes' + description: + - If C(false), SSL certificates will not be validated. This + should only set to C(false) used on personally controlled + sites using self-signed certificates. + use_shared_logon_authentication: + type: bool + default: 'no' + description: + - Whether or not Shared Logon Authentication will be used. + use_radius_authentication: + type: bool + default: 'no' + description: + - Whether or not users will be authenticated via a RADIUS + server. Valid values are true/false. + connection_number: + type: int + description: + - To support multiple connections for same user specify + - different value for this parameter. + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the + different values to perform actions on a logged-on CyberArk + session. + type: dict +''' + +EXAMPLES = ''' +- name: Logon - use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + use_shared_logon_authentication: yes + +- name: Logon - Not use_shared_logon_authentication + cyberark_authentication: + api_base_url: "{{ web_services_base_url }}" + username: "{{ password_object.password }}" + password: "{{ password_object.passprops.username }}" + use_shared_logon_authentication: no + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: "{{ cyberark_session }}" +''' + +RETURN = ''' +cyberark_session: + description: Authentication facts. + returned: success + type: complex + contains: + api_base_url: + description: + - Base URL for API calls. Returned in the cyberark_session, + so it can be used in subsequent calls. + type: str + returned: always + token: + description: + - The token that identifies the session, encoded in BASE 64. + type: str + returned: always + use_shared_logon_authentication: + description: + - Whether or not Shared Logon Authentication was used to + establish the session. + type: bool + returned: always + validate_certs: + description: Whether or not SSL certificates should be validated. + type: bool + returned: always +''' + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.error import HTTPError +import json + +try: + import httplib +except ImportError: + # Python 3 + import http.client as httplib + + +def processAuthentication(module): + + # Getting parameters from module + + api_base_url = module.params["api_base_url"] + validate_certs = module.params["validate_certs"] + username = module.params["username"] + password = module.params["password"] + new_password = module.params["new_password"] + use_shared_logon = module.params[ + "use_shared_logon_authentication" + ] + use_radius = module.params["use_radius_authentication"] + connection_number = module.params["connection_number"] + state = module.params["state"] + cyberark_session = module.params["cyberark_session"] + + # if in check mode it will not perform password changes + if module.check_mode and new_password is not None: + new_password = None + + # Defining initial values for open_url call + headers = {"Content-Type": "application/json"} + payload = "" + + if state == "present": # Logon Action + + # Different end_points based on the use of shared logon authentication + if use_shared_logon: + + end_point = ("/PasswordVault/WebServices/auth/Shared" + "/RestfulAuthenticationService.svc/Logon") + + else: + + end_point = ("/PasswordVault/WebServices/auth/Cyberark" + "/CyberArkAuthenticationService.svc/Logon") + + # The payload will contain username, password + # and optionally use_radius_authentication and new_password + payload_dict = {"username": username, "password": password} + + if use_radius: + payload_dict["useRadiusAuthentication"] = use_radius + + if new_password is not None: + payload_dict["newPassword"] = new_password + + if connection_number is not None: + payload_dict["connectionNumber"] = connection_number + + payload = json.dumps(payload_dict) + + else: # Logoff Action + + # Get values from cyberark_session already established + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + use_shared_logon = cyberark_session[ + "use_shared_logon_authentication" + ] + headers["Authorization"] = cyberark_session["token"] + + # Different end_points based on the use of shared logon authentication + if use_shared_logon: + end_point = ("/PasswordVault/WebServices/auth/Shared" + "/RestfulAuthenticationService.svc/Logoff") + else: + end_point = ("/PasswordVault/WebServices/auth/Cyberark" + "/CyberArkAuthenticationService.svc/Logoff") + + result = None + changed = False + response = None + + try: + + response = open_url( + api_base_url + end_point, + method="POST", + headers=headers, + data=payload, + validate_certs=validate_certs, + ) + + except (HTTPError, httplib.HTTPException) as http_exception: + + module.fail_json( + msg=( + "Error while performing authentication." + "Please validate parameters provided, and ability to logon to " + "CyberArk.\n*** end_point=%s%s\n ==> %s" + ) % (api_base_url, end_point, to_text(http_exception)), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing authentication." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + + if response.getcode() == 200: # Success + + if state == "present": # Logon Action + + # Result token from REST Api uses a different key based + # the use of shared logon authentication + token = None + try: + if use_shared_logon: + token = json.loads(response.read())["LogonResult"] + else: + token = json.loads(response.read())["CyberArkLogonResult"] + except Exception as e: + module.fail_json( + msg="Error obtaining token\n%s" % (to_text(e)), + payload=payload, + headers=headers, + status_code=-1, + ) + + # Preparing result of the module + result = { + "cyberark_session": { + "token": token, + "api_base_url": api_base_url, + "validate_certs": validate_certs, + "use_shared_logon_authentication": use_shared_logon, + } + } + + if new_password is not None: + # Only marks change if new_password was received resulting + # in a password change + changed = True + + else: # Logoff Action clears cyberark_session + + result = {"cyberark_session": {}} + + return (changed, result, response.getcode()) + + else: + module.fail_json( + msg="error in end_point=>" + end_point, headers=headers + ) + + +def main(): + + fields = { + "api_base_url": {"type": "str"}, + "validate_certs": {"type": "bool", "default": "true"}, + "username": {"type": "str"}, + "password": {"type": "str", "no_log": True}, + "new_password": {"type": "str", "no_log": True}, + "use_shared_logon_authentication": {"default": False, "type": "bool"}, + "use_radius_authentication": {"default": False, "type": "bool"}, + "connection_number": {"type": "int"}, + "state": { + "type": "str", + "choices": ["present", "absent"], + "default": "present", + }, + "cyberark_session": {"type": "dict"}, + } + + mutually_exclusive = [ + ["use_shared_logon_authentication", "use_radius_authentication"], + ["use_shared_logon_authentication", "new_password"], + ["api_base_url", "cyberark_session"], + ["cyberark_session", "username", "use_shared_logon_authentication"], + ] + + required_if = [ + ("state", "present", ["api_base_url"]), + ("state", "absent", ["cyberark_session"]), + ] + + required_together = [["username", "password"]] + + module = AnsibleModule( + argument_spec=fields, + mutually_exclusive=mutually_exclusive, + required_if=required_if, + required_together=required_together, + supports_check_mode=True, + ) + + (changed, result, status_code) = processAuthentication(module) + + module.exit_json( + changed=changed, + ansible_facts=result, + status_code=status_code + ) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_credential.py b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_credential.py new file mode 100644 index 00000000..ab832192 --- /dev/null +++ b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_credential.py @@ -0,0 +1,350 @@ +#!/usr/bin/python +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "community", +} + +DOCUMENTATION = """ +--- +module: cyberark_credential +short_description: Credential retrieval using AAM Central Credential Provider. +author: + - Edward Nunez (@enunez-cyberark) + - CyberArk BizDev (@cyberark-bizdev) + - Erasmo Acosta (@erasmix) + - James Stutes (@JimmyJamCABD) +version_added: 2.4 +description: + - Creates a URI for retrieving a credential from a password object stored + in the Cyberark Vault. The request uses the Privileged Account Security + Web Services SDK through the Central Credential Provider by requesting + access with an Application ID. + +options: + api_base_url: + type: str + required: true + description: + - A string containing the base URL of the server hosting the + Central Credential Provider. + validate_certs: + type: bool + required: false + default: true + description: + - If C(false), SSL certificate chain will not be validated. This + should only set to C(true) if you have a root CA certificate + installed on each node. + app_id: + type: str + required: true + description: + - A string containing the Application ID authorized for retrieving + the credential. + query: + type: str + required: true + description: + - A string containing details of the object being queried; + - Possible parameters could be Safe, Folder, Object + - (internal account name), UserName, Address, Database, + - PolicyID. + connection_timeout: + type: int + required: false + default: '30' + description: + - An integer value of the allowed time before the request returns + failed. + query_format: + type: str + required: false + default: Exact + choices: [Exact, Regexp] + description: + - The format for which your Query will be received by the CCP. + fail_request_on_password_change: + type: bool + required: false + default: false + description: + - A boolean parameter for completing the request in the middle of + a password change of the requested credential. + client_cert: + type: str + required: false + description: + - A string containing the file location and name of the client + certificate used for authentication. + client_key: + type: str + required: false + description: + - A string containing the file location and name of the private + key of the client certificate used for authentication. + reason: + type: str + required: false + description: + - Reason for requesting credential if required by policy; + - It must be specified if the Policy managing the object + - requires it. +""" + +EXAMPLES = """ + tasks: + - name: credential retrieval basic + cyberark_credential: + api_base_url: "http://10.10.0.1" + app_id: "TestID" + query: "Safe=test;UserName=admin" + register: result + + - name: credential retrieval advanced + cyberark_credential: + api_base_url: "https://components.cyberark.local" + validate_certs: yes + client_cert: /etc/pki/ca-trust/source/client.pem + client_key: /etc/pki/ca-trust/source/priv-key.pem + app_id: "TestID" + query: "Safe=test;UserName=admin" + connection_timeout: 60 + query_format: Exact + fail_request_on_password_change: True + reason: "requesting credential for Ansible deployment" + register: result + +""" + +RETURN = """ +changed: + description: + - Identify if the playbook run resulted in a change to the account in + any way. + returned: always + type: bool +failed: + description: Whether playbook run resulted in a failure of any kind. + returned: always + type: bool +status_code: + description: Result HTTP Status code. + returned: success + type: int + sample: "200, 201, -1, 204" +result: + description: A json dump of the resulting action. + returned: success + type: complex + contains: + Address: + description: The target address of the credential being queried + type: str + returned: if required + Content: + description: The password for the object being queried + type: str + returned: always + CreationMethod: + description: This is how the object was created in the Vault + type: str + returned: always + DeviceType: + description: + - An internal File Category for more granular management of + Platforms. + type: str + returned: always + Folder: + description: + - The folder within the Safe where the credential is stored. + type: str + returned: always + Name: + description: + - The Cyberark unique object ID of the credential being + queried. + type: str + returned: always + PasswordChangeInProcess: + description: If the password has a change flag placed by the CPM + type: bool + returned: always + PolicyID: + description: Whether or not SSL certificates should be validated. + type: str + returned: if assigned to a policy + Safe: + description: The safe where the queried credential is stored + type: string + returned: always + Username: + description: The username of the credential being queried + type: str + returned: if required + LogonDomain: + description: The Address friendly name resolved by the CPM + type: str + returned: if populated + CPMDisabled: + description: + - A description of why this vaulted credential is not being + managed by the CPM. + type: str + returned: if CPM management is disabled and a reason is given +""" + +from ansible.module_utils._text import to_text +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.urls import open_url +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.six.moves.urllib.parse import quote +import json + +try: + import httplib +except ImportError: + # Python 3 + import http.client as httplib + + +def retrieve_credential(module): + + # Getting parameters from module + + api_base_url = module.params["api_base_url"] + validate_certs = module.params["validate_certs"] + app_id = module.params["app_id"] + query = module.params["query"] + connection_timeout = module.params["connection_timeout"] + query_format = module.params["query_format"] + fail_request_on_password_change = module.params[ + "fail_request_on_password_change" + ] + client_cert = None + client_key = None + + if "client_cert" in module.params: + client_cert = module.params["client_cert"] + if "client_key" in module.params: + client_key = module.params["client_key"] + + end_point = ( + "/AIMWebService/api/Accounts?AppId=%s&Query=%s&" + "ConnectionTimeout=%s&QueryFormat=%s" + "&FailRequestOnPasswordChange=%s" + ) % ( + quote(app_id), + quote(query), + connection_timeout, + query_format, + fail_request_on_password_change, + ) + + if "reason" in module.params and module.params["reason"] is not None: + reason = quote(module.params["reason"]) + end_point = end_point + "&reason=%s" % reason + + result = None + response = None + + try: + + response = open_url( + api_base_url + end_point, + method="GET", + validate_certs=validate_certs, + client_cert=client_cert, + client_key=client_key, + ) + + except (HTTPError, httplib.HTTPException) as http_exception: + + module.fail_json( + msg=( + "Error while retrieving credential." + "Please validate parameters provided, and permissions for " + "the application and provider in CyberArk." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, to_text(http_exception)) + ), + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while retrieving credential." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + status_code=-1, + ) + + if response.getcode() == 200: # Success + + # Result token from REST Api uses a different key based + try: + result = json.loads(response.read()) + except Exception as exc: + module.fail_json( + msg=( + "Error obtain cyberark credential result " + "from http body\n%s" + ) % (to_text(exc)), + status_code=-1, + ) + + return (result, response.getcode()) + + else: + module.fail_json(msg="error in end_point=>" + end_point) + + +def main(): + + fields = { + "api_base_url": {"required": True, "type": "str"}, + "app_id": {"required": True, "type": "str"}, + "query": {"required": True, "type": "str"}, + "reason": {"required": False, "type": "str"}, + "connection_timeout": { + "required": False, + "type": "int", + "default": 30 + }, + "query_format": { + "required": False, + "type": "str", + "choices": ["Exact", "Regexp"], + "default": "Exact", + }, + "fail_request_on_password_change": { + "required": False, + "type": "bool", + "default": False, + }, + "validate_certs": {"type": "bool", "default": True}, + "client_cert": {"type": "str", "required": False}, + "client_key": {"type": "str", "required": False}, + } + + module = AnsibleModule(argument_spec=fields, supports_check_mode=True) + + (result, status_code) = retrieve_credential(module) + + module.exit_json(changed=False, result=result, status_code=status_code) + + +if __name__ == "__main__": + main() diff --git a/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_user.py b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_user.py new file mode 100644 index 00000000..3939378a --- /dev/null +++ b/collections-debian-merged/ansible_collections/cyberark/pas/plugins/modules/cyberark_user.py @@ -0,0 +1,619 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# Copyright: (c) 2017, Ansible Project +# GNU General Public License v3.0+ +# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import absolute_import, division, print_function + +__metaclass__ = type + +ANSIBLE_METADATA = { + "metadata_version": "1.1", + "status": ["preview"], + "supported_by": "certified", +} + +DOCUMENTATION = r""" +--- +module: cyberark_user +short_description: CyberArk User Management using PAS Web Services SDK. +author: + - Edward Nunez (@enunez-cyberark) + - Cyberark Bizdev (@cyberark-bizdev) + - Erasmo Acosta (@erasmix) + - James Stutes (@jimmyjamcabd) +version_added: 2.4 +description: + - CyberArk User Management using PAS Web Services SDK, + It currently supports the following actions Get User Details, Add User, + Update User, Delete User. + +options: + username: + description: + - The name of the user who will be queried (for details), added, + updated or deleted. + type: str + required: True + state: + description: + - Specifies the state needed for the user present for create user, + absent for delete user. + type: str + choices: [ absent, present ] + default: present + logging_level: + description: + - Parameter used to define the level of troubleshooting output to + the C(logging_file) value. + required: true + choices: [NOTSET, DEBUG, INFO] + default: NOTSET + type: str + logging_file: + description: + - Setting the log file name and location for troubleshooting logs. + required: false + default: /tmp/ansible_cyberark.log + type: str + cyberark_session: + description: + - Dictionary set by a CyberArk authentication containing the + different values to perform actions on a logged-on CyberArk + session, please see M(cyberark_authentication) module for an + example of cyberark_session. + type: dict + required: True + initial_password: + description: + - The password that the new user will use to log on the first time. + - This password must meet the password policy requirements. + - This parameter is required when state is present -- Add User. + type: str + new_password: + description: + - The user updated password. Make sure that this password meets + the password policy requirements. + type: str + email: + description: + - The user email address. + type: str + first_name: + description: + - The user first name. + type: str + last_name: + description: + - The user last name. + type: str + change_password_on_the_next_logon: + description: + - Whether or not the user must change their password in their + next logon. + type: bool + default: no + expiry_date: + description: + - The date and time when the user account will expire and become + disabled. + type: str + user_type_name: + description: + - The type of user. + - The parameter defaults to C(EPVUser). + type: str + disabled: + description: + - Whether or not the user will be disabled. + type: bool + default: no + location: + description: + - The Vault Location for the user. + type: str + group_name: + description: + - The name of the group the user will be added to. + type: str +""" + +EXAMPLES = r""" +- name: Logon to CyberArk Vault using PAS Web Services SDK + cyberark_authentication: + api_base_url: https://components.cyberark.local + use_shared_logon_authentication: yes + +- name: Create user & immediately add it to a group + cyberark_user: + username: username + initial_password: password + user_type_name: EPVUser + change_password_on_the_next_logon: no + group_name: GroupOfUser + state: present + cyberark_session: '{{ cyberark_session }}' + +- name: Make sure user is present and reset user credential if present + cyberark_user: + username: Username + new_password: password + disabled: no + state: present + cyberark_session: '{{ cyberark_session }}' + +- name: Logoff from CyberArk Vault + cyberark_authentication: + state: absent + cyberark_session: '{{ cyberark_session }}' +""" + +RETURN = r""" +changed: + description: Whether there was a change done. + type: bool + returned: always +cyberark_user: + description: Dictionary containing result properties. + returned: always + type: complex + contains: + result: + description: user properties when state is present + type: dict + returned: success +status_code: + description: Result HTTP Status code + returned: success + type: int + sample: 200 +""" + +import json + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils._text import to_text +from ansible.module_utils.six.moves import http_client as httplib +from ansible.module_utils.six.moves.urllib.error import HTTPError +from ansible.module_utils.urls import open_url +import logging +import urllib + + +def user_details(module): + + # Get username from module parameters, and api base url + # along with validate_certs from the cyberark_session established + username = module.params["username"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{0}".format( + username + ) + headers = {"Content-Type": "application/json"} + headers["Authorization"] = cyberark_session["token"] + + try: + + response = open_url( + api_base_url + end_point, + method="GET", + headers=headers, + validate_certs=validate_certs, + ) + result = {"result": json.loads(response.read())} + + return (False, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + if http_exception.code == 404: + return (False, None, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing user_details." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, to_text(http_exception)) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_details." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def user_add_or_update(module, HTTPMethod, existing_info): + + # Get username from module parameters, and api base url + # along with validate_certs from the cyberark_session established + username = module.params["username"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, paylod, and headers + result = {} + payload = {} + headers = { + "Content-Type": "application/json", + "Authorization": cyberark_session["token"], + } + + # end_point and payload sets different depending on POST/PUT + # for POST -- create -- payload contains username + # for PUT -- update -- username is part of the endpoint + if HTTPMethod == "POST": + end_point = "/PasswordVault/WebServices/PIMServices.svc/Users" + payload["UserName"] = username + if ( + "initial_password" in module.params.keys() + and module.params["initial_password"] is not None + ): + payload["InitialPassword"] = module.params["initial_password"] + + elif HTTPMethod == "PUT": + end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{0}" + end_point = end_point.format(username) + + # --- Optionally populate payload based on parameters passed --- + if ( + "new_password" in module.params + and module.params["new_password"] is not None + ): + payload["NewPassword"] = module.params["new_password"] + + if "email" in module.params and module.params["email"] is not None: + payload["Email"] = module.params["email"] + + if ( + "first_name" in module.params + and module.params["first_name"] is not None + ): + payload["FirstName"] = module.params["first_name"] + + if "last_name" in module.params and module.params["last_name"] is not None: + payload["LastName"] = module.params["last_name"] + + if ( + "change_password_on_the_next_logon" in module.params + and module.params["change_password_on_the_next_logon"] is not None + ): + payload["ChangePasswordOnTheNextLogon"] = module.params[ + "change_password_on_the_next_logon" + ] + + if ( + "expiry_date" in module.params + and module.params["expiry_date"] is not None + ): + payload["ExpiryDate"] = module.params["expiry_date"] + + if ( + "user_type_name" in module.params + and module.params["user_type_name"] is not None + ): + payload["UserTypeName"] = module.params["user_type_name"] + + if "disabled" in module.params and module.params["disabled"] is not None: + payload["Disabled"] = module.params["disabled"] + + if "location" in module.params and module.params["location"] is not None: + payload["Location"] = module.params["location"] + + # -------------------------------------------------------------- + logging.debug( + "HTTPMethod = " + + HTTPMethod + + " module.params = " + + json.dumps(module.params) + ) + logging.debug("Existing Info: %s", json.dumps(existing_info)) + logging.debug("payload => %s", json.dumps(payload)) + + if HTTPMethod == "PUT" and ( + "new_password" not in module.params + or module.params["new_password"] is None + ): + logging.info("Verifying if needs to be updated") + proceed = False + updateable_fields = [ + "Email", + "FirstName", + "LastName", + "ChangePasswordOnTheNextLogon", + "ExpiryDate", + "UserTypeName", + "Disabled", + "Location", + ] + for field_name in updateable_fields: + logging.debug("#### field_name : %s", field_name) + if ( + field_name in payload + and field_name in existing_info + and payload[field_name] != existing_info[field_name] + ): + logging.debug("Changing value for %s", field_name) + proceed = True + else: + proceed = True + + if proceed: + logging.info("Proceeding to either update or create") + try: + + # execute REST action + response = open_url( + api_base_url + end_point, + method=HTTPMethod, + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + ) + + result = {"result": json.loads(response.read())} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + module.fail_json( + msg=( + "Error while performing user_add_or_update." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, to_text(http_exception)) + ), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_add_or_update." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + else: + return (False, existing_info, 200) + + +def user_delete(module): + + # Get username from module parameters, and api base url + # along with validate_certs from the cyberark_session established + username = module.params["username"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, and headers + result = {} + end_point = ( + "/PasswordVault/WebServices/PIMServices.svc/Users/{0}" + ).format(username) + + headers = {"Content-Type": "application/json"} + headers["Authorization"] = cyberark_session["token"] + + try: + + # execute REST action + response = open_url( + api_base_url + end_point, + method="DELETE", + headers=headers, + validate_certs=validate_certs, + ) + + result = {"result": {}} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + exception_text = to_text(http_exception) + if http_exception.code == 404 and "ITATS003E" in exception_text: + # User does not exist + result = {"result": {}} + return (False, result, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing user_delete." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, exception_text) + ), + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_delete." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + headers=headers, + status_code=-1, + ) + + +def user_add_to_group(module): + + # Get username, and groupname from module parameters, and api base url + # along with validate_certs from the cyberark_session established + username = module.params["username"] + group_name = module.params["group_name"] + cyberark_session = module.params["cyberark_session"] + api_base_url = cyberark_session["api_base_url"] + validate_certs = cyberark_session["validate_certs"] + + # Prepare result, end_point, headers and payload + result = {} + end_point = ( + "/PasswordVault/WebServices/PIMServices.svc/Groups/{0}/Users" + ).format( + urllib.quote(group_name) + ) + + headers = {"Content-Type": "application/json"} + headers["Authorization"] = cyberark_session["token"] + payload = {"UserName": username} + + try: + + # execute REST action + response = open_url( + api_base_url + end_point, + method="POST", + headers=headers, + data=json.dumps(payload), + validate_certs=validate_certs, + ) + + result = {"result": {}} + + return (True, result, response.getcode()) + + except (HTTPError, httplib.HTTPException) as http_exception: + + exception_text = to_text(http_exception) + if http_exception.code == 409 and "ITATS262E" in exception_text: + # User is already member of Group + return (False, None, http_exception.code) + else: + module.fail_json( + msg=( + "Error while performing user_add_to_group." + "Please validate parameters provided." + "\n*** end_point=%s%s\n ==> %s" + % (api_base_url, end_point, exception_text) + ), + payload=payload, + headers=headers, + status_code=http_exception.code, + ) + + except Exception as unknown_exception: + + module.fail_json( + msg=( + "Unknown error while performing user_add_to_group." + "\n*** end_point=%s%s\n%s" + % (api_base_url, end_point, to_text(unknown_exception)) + ), + payload=payload, + headers=headers, + status_code=-1, + ) + + +def main(): + + module = AnsibleModule( + argument_spec=dict( + username=dict(type="str", required=True), + state=dict( + type="str", + default="present", + choices=["absent", "present"] + ), + logging_level=dict( + type="str", + default="NOTSET", + choices=["NOTSET", "DEBUG", "INFO"] + ), + logging_file=dict(type="str", default="/tmp/ansible_cyberark.log"), + cyberark_session=dict(type="dict", required=True), + initial_password=dict(type="str", no_log=True), + new_password=dict(type="str", no_log=True), + email=dict(type="str"), + first_name=dict(type="str"), + last_name=dict(type="str"), + change_password_on_the_next_logon=dict(type="bool"), + expiry_date=dict(type="str"), + user_type_name=dict(type="str"), + disabled=dict(type="bool"), + location=dict(type="str"), + group_name=dict(type="str"), + ) + ) + + if module.params["logging_level"] is not None: + logging.basicConfig( + filename=module.params["logging_file"], + level=module.params["logging_level"] + ) + + logging.info("Starting Module") + + state = module.params["state"] + group_name = module.params["group_name"] + + if state == "present": + (changed, result, status_code) = user_details(module) + + if status_code == 200: + # User already exists + + (changed, result, status_code) = user_add_or_update( + module, "PUT", result["result"] + ) + + if group_name is not None: + # If user exists, add to group if needed + (changed_group, no_result, no_status_code) = user_add_to_group(module) + changed = changed or changed_group + + elif status_code == 404: + # User does not exist, proceed to create it + (changed, result, status_code) = user_add_or_update( + module, + "POST", + None + ) + + if status_code == 201 and group_name is not None: + # If user was created, add to group if needed + (changed, no_result, no_status_code) = user_add_to_group(module) + + elif state == "absent": + (changed, result, status_code) = user_delete(module) + + module.exit_json( + changed=changed, + cyberark_user=result, + status_code=status_code + ) + + +if __name__ == "__main__": + main() |