diff options
Diffstat (limited to 'ansible_collections/community/general/plugins/lookup/tss.py')
-rw-r--r-- | ansible_collections/community/general/plugins/lookup/tss.py | 195 |
1 files changed, 169 insertions, 26 deletions
diff --git a/ansible_collections/community/general/plugins/lookup/tss.py b/ansible_collections/community/general/plugins/lookup/tss.py index 935b5f4b4..80105ff71 100644 --- a/ansible_collections/community/general/plugins/lookup/tss.py +++ b/ansible_collections/community/general/plugins/lookup/tss.py @@ -13,10 +13,10 @@ short_description: Get secrets from Thycotic Secret Server version_added: 1.0.0 description: - Uses the Thycotic Secret Server Python SDK to get Secrets from Secret - Server using token authentication with I(username) and I(password) on - the REST API at I(base_url). + Server using token authentication with O(username) and O(password) on + the REST API at O(base_url). - When using self-signed certificates the environment variable - C(REQUESTS_CA_BUNDLE) can be set to a file containing the trusted certificates + E(REQUESTS_CA_BUNDLE) can be set to a file containing the trusted certificates (in C(.pem) format). - For example, C(export REQUESTS_CA_BUNDLE='/etc/ssl/certs/ca-bundle.trust.crt'). requirements: @@ -26,8 +26,32 @@ options: description: The integer ID of the secret. required: true type: int + secret_path: + description: Indicate a full path of secret including folder and secret name when the secret ID is set to 0. + required: false + type: str + version_added: 7.2.0 + fetch_secret_ids_from_folder: + description: + - Boolean flag which indicates whether secret ids are in a folder is fetched by folder ID or not. + - V(true) then the terms will be considered as a folder IDs. Otherwise (default), they are considered as secret IDs. + required: false + type: bool + version_added: 7.1.0 + fetch_attachments: + description: + - Boolean flag which indicates whether attached files will get downloaded or not. + - The download will only happen if O(file_download_path) has been provided. + required: false + type: bool + version_added: 7.0.0 + file_download_path: + description: Indicate the file attachment download location. + required: false + type: path + version_added: 7.0.0 base_url: - description: The base URL of the server, e.g. C(https://localhost/SecretServer). + description: The base URL of the server, for example V(https://localhost/SecretServer). env: - name: TSS_BASE_URL ini: @@ -44,7 +68,7 @@ options: password: description: - The password associated with the supplied username. - - Required when I(token) is not provided. + - Required when O(token) is not provided. env: - name: TSS_PASSWORD ini: @@ -54,7 +78,7 @@ options: default: "" description: - The domain with which to request the OAuth2 Access Grant. - - Optional when I(token) is not provided. + - Optional when O(token) is not provided. - Requires C(python-tss-sdk) version 1.0.0 or greater. env: - name: TSS_DOMAIN @@ -66,7 +90,7 @@ options: token: description: - Existing token for Thycotic authorizer. - - If provided, I(username) and I(password) are not needed. + - If provided, O(username) and O(password) are not needed. - Requires C(python-tss-sdk) version 1.0.0 or greater. env: - name: TSS_TOKEN @@ -157,39 +181,101 @@ EXAMPLES = r""" tasks: - ansible.builtin.debug: msg: the password is {{ secret_password }} + +# Private key stores into certificate file which is attached with secret. +# If fetch_attachments=True then private key file will be download on specified path +# and file content will display in debug message. +- hosts: localhost + vars: + secret: >- + {{ + lookup( + 'community.general.tss', + 102, + fetch_attachments=True, + file_download_path='/home/certs', + base_url='https://secretserver.domain.com/SecretServer/', + token='thycotic_access_token' + ) + }} + tasks: + - ansible.builtin.debug: + msg: > + the private key is {{ + (secret['items'] + | items2dict(key_name='slug', + value_name='itemValue'))['private-key'] + }} + +# If fetch_secret_ids_from_folder=true then secret IDs are in a folder is fetched based on folder ID +- hosts: localhost + vars: + secret: >- + {{ + lookup( + 'community.general.tss', + 102, + fetch_secret_ids_from_folder=true, + base_url='https://secretserver.domain.com/SecretServer/', + token='thycotic_access_token' + ) + }} + tasks: + - ansible.builtin.debug: + msg: > + the secret id's are {{ + secret + }} + +# If secret ID is 0 and secret_path has value then secret is fetched by secret path +- hosts: localhost + vars: + secret: >- + {{ + lookup( + 'community.general.tss', + 0, + secret_path='\folderName\secretName' + base_url='https://secretserver.domain.com/SecretServer/', + username='user.name', + password='password' + ) + }} + tasks: + - ansible.builtin.debug: + msg: > + the password is {{ + (secret['items'] + | items2dict(key_name='slug', + value_name='itemValue'))['password'] + }} """ import abc - +import os from ansible.errors import AnsibleError, AnsibleOptionsError from ansible.module_utils import six from ansible.plugins.lookup import LookupBase from ansible.utils.display import Display try: - from thycotic.secrets.server import SecretServer, SecretServerError + from delinea.secrets.server import SecretServer, SecretServerError, PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer HAS_TSS_SDK = True + HAS_DELINEA_SS_SDK = True + HAS_TSS_AUTHORIZER = True except ImportError: try: - from delinea.secrets.server import SecretServer, SecretServerError + from thycotic.secrets.server import SecretServer, SecretServerError, PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer HAS_TSS_SDK = True + HAS_DELINEA_SS_SDK = False + HAS_TSS_AUTHORIZER = True except ImportError: SecretServer = None SecretServerError = None HAS_TSS_SDK = False - -try: - from thycotic.secrets.server import PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer - - HAS_TSS_AUTHORIZER = True -except ImportError: - try: - from delinea.secrets.server import PasswordGrantAuthorizer, DomainPasswordGrantAuthorizer, AccessTokenAuthorizer - - HAS_TSS_AUTHORIZER = True - except ImportError: + HAS_DELINEA_SS_SDK = False PasswordGrantAuthorizer = None DomainPasswordGrantAuthorizer = None AccessTokenAuthorizer = None @@ -211,13 +297,49 @@ class TSSClient(object): else: return TSSClientV0(**server_parameters) - def get_secret(self, term): + def get_secret(self, term, secret_path, fetch_file_attachments, file_download_path): display.debug("tss_lookup term: %s" % term) - secret_id = self._term_to_secret_id(term) - display.vvv(u"Secret Server lookup of Secret with ID %d" % secret_id) + if secret_id == 0 and secret_path: + fetch_secret_by_path = True + display.vvv(u"Secret Server lookup of Secret with path %s" % secret_path) + else: + fetch_secret_by_path = False + display.vvv(u"Secret Server lookup of Secret with ID %d" % secret_id) + + if fetch_file_attachments: + if fetch_secret_by_path: + obj = self._client.get_secret_by_path(secret_path, fetch_file_attachments) + else: + obj = self._client.get_secret(secret_id, fetch_file_attachments) + for i in obj['items']: + if file_download_path and os.path.isdir(file_download_path): + if i['isFile']: + try: + file_content = i['itemValue'].content + with open(os.path.join(file_download_path, str(obj['id']) + "_" + i['slug']), "wb") as f: + f.write(file_content) + except ValueError: + raise AnsibleOptionsError("Failed to download {0}".format(str(i['slug']))) + except AttributeError: + display.warning("Could not read file content for {0}".format(str(i['slug']))) + finally: + i['itemValue'] = "*** Not Valid For Display ***" + else: + raise AnsibleOptionsError("File download path does not exist") + return obj + else: + if fetch_secret_by_path: + return self._client.get_secret_by_path(secret_path, False) + else: + return self._client.get_secret_json(secret_id) + + def get_secret_ids_by_folderid(self, term): + display.debug("tss_lookup term: %s" % term) + folder_id = self._term_to_folder_id(term) + display.vvv(u"Secret Server lookup of Secret id's with Folder ID %d" % folder_id) - return self._client.get_secret_json(secret_id) + return self._client.get_secret_ids_by_folderid(folder_id) @staticmethod def _term_to_secret_id(term): @@ -226,6 +348,13 @@ class TSSClient(object): except ValueError: raise AnsibleOptionsError("Secret ID must be an integer") + @staticmethod + def _term_to_folder_id(term): + try: + return int(term) + except ValueError: + raise AnsibleOptionsError("Folder ID must be an integer") + class TSSClientV0(TSSClient): def __init__(self, **server_parameters): @@ -294,6 +423,20 @@ class LookupModule(LookupBase): ) try: - return [tss.get_secret(term) for term in terms] + if self.get_option("fetch_secret_ids_from_folder"): + if HAS_DELINEA_SS_SDK: + return [tss.get_secret_ids_by_folderid(term) for term in terms] + else: + raise AnsibleError("latest python-tss-sdk must be installed to use this plugin") + else: + return [ + tss.get_secret( + term, + self.get_option("secret_path"), + self.get_option("fetch_attachments"), + self.get_option("file_download_path"), + ) + for term in terms + ] except SecretServerError as error: raise AnsibleError("Secret Server lookup failure: %s" % error.message) |