Init: mediaserver

This commit is contained in:
2023-02-08 12:13:28 +01:00
parent 848bc9739c
commit f7c23d4ba9
31914 changed files with 6175775 additions and 0 deletions

View File

@@ -0,0 +1,392 @@
#!/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)
version_added: '1.0.0'
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_ldap_authentication:
type: bool
default: 'no'
description:
- Whether or not LDAP will be used.
use_windows_authentication:
type: bool
default: 'no'
description:
- Whether or not Windows will be used.
use_cyberark_authentication:
type: bool
default: 'no'
description:
- Whether or not LDAP 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.
concurrentSession:
type: bool
default: False
description:
- Whether or not to allow concurrent sessions for the same user.
cyberark_session:
description:
- Dictionary set by a CyberArk authentication containing the
different values to perform actions on a logged-on CyberArk
session.
type: dict
timeout:
description:
- Allows you set a timeout for when your authenticating to Cyberark
default: 10
type: int
"""
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
from ansible.module_utils.six.moves.http_client import HTTPException
import json
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_radius = module.params["use_radius_authentication"]
use_ldap = module.params["use_ldap_authentication"]
use_windows = module.params["use_windows_authentication"]
use_cyberark = module.params["use_cyberark_authentication"]
# connection_number = module.params["connection_number"]
state = module.params["state"]
cyberark_session = module.params["cyberark_session"]
concurrentSession = module.params["concurrentSession"]
timeout = module.params["timeout"]
# 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",
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
payload = ""
if state == "present": # Logon Action
# Different end_points based on the use of desired method of auth
if use_ldap:
end_point = "/PasswordVault/API/Auth/LDAP/Logon"
payload_dict = {"username": username, "password": password}
elif use_radius:
end_point = "/PasswordVault/API/Auth/radius/Logon"
elif use_windows:
end_point = "/PasswordVault/API/auth/Windows/Logon"
else:
use_cyberark = True
end_point = "/PasswordVault/API/Auth/CyberArk/Logon"
# The payload will contain username, password
# and optionally use_radius_authentication and new_password
payload_dict = {"username": username, "password": password}
if new_password is not None and use_cyberark:
payload_dict["newPassword"] = new_password
# COMMENT: I dont know what this is for and the old api seems like it didnt have this field
# if connection_number is not None:
# payload_dict["connectionNumber"] = connection_number
if concurrentSession:
payload_dict["concurrentSession"] = True
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"]
headers["Authorization"] = cyberark_session["token"]
# All off the logoff with the same endpoint
end_point = "/PasswordVault/API/Auth/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,
timeout=timeout,
)
except (HTTPError, 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 = ""
try:
token = str(json.loads(response.read()))
# the new one just returns a token
# if use:
# 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,
}
}
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_radius_authentication": {"default": False, "type": "bool"},
"use_windows_authentication": {"default": False, "type": "bool"},
"use_ldap_authentication": {"default": False, "type": "bool"},
"use_cyberark_authentication": {"default": False, "type": "bool"},
"concurrentSession": {"default": False, "type": "bool"},
"connection_number": {"type": "int"},
"state": {
"type": "str",
"choices": ["present", "absent"],
"default": "present",
},
"cyberark_session": {"type": "dict"},
"timeout": {"default": 10, "type": "int"},
}
# cyberark and radius -> mutually_exclusive is cyberark and ldap
# ldap and radius
# windows has to be by itself
mutually_exclusive = [
[
"use_windows_authentication",
"use_ldap_authentication",
"use_cyberark_authentication",
"use_radius_authentication",
],
["use_radius_authentication", "new_password"],
["use_windows_authentication", "new_password"],
["use_ldap_authentication", "new_password"],
["api_base_url", "cyberark_session"],
]
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()

View File

@@ -0,0 +1,337 @@
#!/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: '1.0.0'
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: str
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
from ansible.module_utils.six.moves.http_client import HTTPException
import json
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, 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, "no_log": True},
}
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()

View File

@@ -0,0 +1,782 @@
#!/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: '1.0.0'
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: false
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.pas.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
domain_name:
description:
- The name of the user domain.
type: str
member_type:
description:
- The type of member.
type: str
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.
- Causes an additional lookup in cyberark
- Will be ignored if vault_id is used
- Will cause a failure if group is missing or more than one group with that name exists
type: str
timeout:
description:
- How long to wait for the server to send data before giving up
type: float
default: 10
vault_id:
description:
- The ID of the user group to add the user to
- Prefered over group_name
type: int
authorization:
description:
- A list of authorization options for this user.
- Options can include AddSafes and AuditUsers
- The default provides backwards compatability with older versions of the collection
type: list
elements: str
default:
- AddSafes
- AuditUsers
"""
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
from ansible.module_utils.six.moves.urllib.parse import quote
import logging
def construct_url(api_base_url, end_point):
return "{baseurl}/{endpoint}".format(baseurl=api_base_url.rstrip("/"), endpoint=end_point.lstrip("/"))
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/{pusername}".format(pusername=username)
url = construct_url(api_base_url, end_point)
headers = {
"Content-Type": "application/json",
"Authorization": cyberark_session["token"],
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
try:
response = open_url(
url,
method="GET",
headers=headers,
validate_certs=validate_certs,
timeout=module.params['timeout'],
)
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\n ==> %s"
% (url, 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\n%s"
% (url, 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"],
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
# 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/api/Users"
payload["UserName"] = username
if (
"initial_password" in list(module.params.keys())
and module.params["initial_password"] is not None
):
payload["InitialPassword"] = module.params["initial_password"]
elif HTTPMethod == "PUT":
# With the put in this old format, we can not update the vaultAuthorization
end_point = "/PasswordVault/WebServices/PIMServices.svc/Users/{pusername}".format(pusername=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"]
# In API V2 the parameter is called userType, V2 ignores the UserTypeName
payload["userType"] = 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"]
if module.params.get("authorization", None) is not None:
payload["vaultAuthorization"] = module.params["authorization"]
# --------------------------------------------------------------
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",
"UserTypeName",
"vaultAuthorization",
]
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")
url = construct_url(api_base_url, end_point)
try:
# execute REST action
response = open_url(
url,
method=HTTPMethod,
headers=headers,
data=json.dumps(payload),
validate_certs=validate_certs,
timeout=module.params['timeout'],
)
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\n ==> %s"
% (url, 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\n%s"
% (url, to_text(unknown_exception))
),
payload=payload,
headers=headers,
status_code=-1,
)
else:
return (False, existing_info, 200)
def resolve_username_to_id(module):
username = module.params["username"]
cyberark_session = module.params["cyberark_session"]
api_base_url = cyberark_session["api_base_url"]
validate_certs = cyberark_session["validate_certs"]
url = construct_url(api_base_url, "PasswordVault/api/Users?search={pusername}".format(pusername=username))
headers = {
"Content-Type": "application/json",
"Authorization": cyberark_session["token"],
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
try:
response = open_url(
url,
method="GET",
headers=headers,
validate_certs=validate_certs,
timeout=module.params['timeout'],
)
users = json.loads(response.read())
# Return None if the user does not exist
user_id = None
# Say we have two users: 'someone' and 'someoneelse', a search on someone will return both
# So we will lopp over and see if the username returned matches the username we searched for
# If so, and we somehow found more than one raise an error
for user in users['Users']:
if user['username'] == username:
if user_id is None:
user_id = user['id']
else:
module.fail_json(msg=("Found more than one user matching %s, this should be impossible" % (username)))
# If we made it here we had 1 or 0 users, return them
logging.debug("Resolved username {%s} to ID {%s}", username, user_id)
return user_id
except (HTTPError, httplib.HTTPException) as http_exception:
exception_text = to_text(http_exception)
module.fail_json(msg=(
"Error while performing user_search."
"Please validate parameters provided."
"\n*** end_point=%s\n ==> %s"
% (url, exception_text)),
headers=headers,
status_code=http_exception.code,
)
except Exception as unknown_exception:
module.fail_json(msg=(
"Unknown error while performing user search."
"\n*** end_point=%s\n%s"
% (url, to_text(unknown_exception))),
headers=headers,
status_code=-1,
)
def user_delete(module):
# Get username from module parameters, and api base url
# along with validate_certs from the cyberark_session established
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 = {}
vault_user_id = resolve_username_to_id(module)
# If the user was not found by username we can return unchanged
if vault_user_id is None:
return (False, result, None)
end_point = ("PasswordVault/api/Users/{pvaultuserid}").format(pvaultuserid=vault_user_id)
headers = {
"Content-Type": "application/json",
"Authorization": cyberark_session["token"],
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
url = construct_url(api_base_url, end_point)
try:
# execute REST action
response = open_url(
url,
method="DELETE",
headers=headers,
validate_certs=validate_certs,
timeout=module.params['timeout'],
)
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\n ==> %s"
% (url, 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\n%s"
% (url, to_text(unknown_exception))
),
headers=headers,
status_code=-1,
)
def resolve_group_name_to_id(module):
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"]
headers = {
"Content-Type": "application/json",
"Authorization": cyberark_session["token"],
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
url = construct_url(api_base_url, "/PasswordVault/api/UserGroups?search={pgroupname}".format(pgroupname=quote(group_name)))
try:
response = open_url(
url,
method="GET",
headers=headers,
validate_certs=validate_certs,
timeout=module.params['timeout'],
)
groups = json.loads(response.read())
# Return None if the user does not exist
group_id = None
# Say we have two groups: 'groupone' and 'grouptwo', a search on group will return both
# So we will lopp over and see if the groupname returned matches the groupsname we searched for
# If so, and we somehow found more than one raise an error
for group in groups['value']:
if group['groupName'] == group_name:
if group_id is None:
group_id = group['id']
else:
module.fail_json(msg=("Found more than one group matching %s. Use vault_id instead" % (group_name)))
# If we made it here we had 1 or 0 users, return them
logging.debug("Resolved group_name %s to ID %s", group_name, group_id)
return group_id
except (HTTPError, httplib.HTTPException) as http_exception:
module.fail_json(msg=(
"Error while looking up group %s.\n*** end_point=%s\n ==> %s"
% (group_name, url, to_text(http_exception))),
payload={},
headers=headers,
status_code=http_exception.code,
)
except Exception as unknown_exception:
module.fail_json(msg=(
"Unknown error while looking up group %s.\n*** end_point=%s\n%s"
% (group_name, url, to_text(unknown_exception))),
payload={},
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
# Not needed for new version
username = module.params["username"]
group_name = module.params["group_name"]
vault_id = module.params["vault_id"]
member_type = (
"Vault"
if module.params["member_type"] is None
else module.params["member_type"]
)
domain_name = module.params["domain_name"] if member_type == "domain" else None
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 = {}
headers = {
"Content-Type": "application/json",
"Authorization": cyberark_session["token"],
"User-Agent": "CyberArk/1.0 (Ansible; cyberark.pas)"
}
# If we went "old school" and were provided a group_name instead of a vault_id we need to resolve it
if group_name and not vault_id:
# If we were given a group_name we need to lookup the vault_id
vault_id = resolve_group_name_to_id(module)
if vault_id is None:
module.fail_json(msg="Unable to find a user group named {pgroupname}, please create that before adding a user to it".format(pgroupname=group_name))
end_point = ("/PasswordVault/api/UserGroups/{pvaultid}/Members").format(pvaultid=vault_id)
# For some reason the group add uses username instead of id
payload = {"memberId": username, "memberType": member_type}
if domain_name:
payload["domainName"] = domain_name
url = construct_url(api_base_url, end_point)
try:
# execute REST action
response = open_url(
url,
method="POST",
headers=headers,
data=json.dumps(payload),
validate_certs=validate_certs,
timeout=module.params['timeout'],
)
result = {"result": {}}
return (True, result, response.getcode())
except (HTTPError, httplib.HTTPException) as http_exception:
exception_text = to_text(http_exception)
exception_body = json.loads(http_exception.read().decode())
if http_exception.code == 409 and ("ITATS262E" in exception_text or exception_body.get("ErrorCode", "") == "PASWS213E"):
# 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\n ==> %s"
% (url, exception_text)
),
payload=payload,
headers=headers,
status_code=http_exception.code,
response=http_exception.read().decode(),
)
except Exception as unknown_exception:
module.fail_json(
msg=(
"Unknown error while performing user_add_to_group."
"\n*** end_point=%s\n%s"
% (url, 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", default=False),
expiry_date=dict(type="str"),
user_type_name=dict(type="str"),
disabled=dict(type="bool", default=False),
location=dict(type="str"),
group_name=dict(type="str"),
vault_id=dict(type="int"),
member_type=dict(type="str"),
domain_name=dict(type="str"),
timeout=dict(type="float", default=10),
authorization=dict(type="list", elements="str", required=False, default=['AddSafes', 'AuditUsers']),
)
)
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"]
vault_id = module.params["vault_id"]
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"]
)
elif status_code == 404:
# User does not exist, proceed to create it
(changed, result, status_code) = user_add_or_update(module, "POST", None)
# Add user to group if needed
if group_name is not None or vault_id is not None:
(group_change, no_result, no_status_code) = user_add_to_group(module)
changed = changed or group_change
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()