Skip to content

Commit cf6311b

Browse files
committed
refactor(lookup): update lookup plugins to use DVLSLookupHelper
1 parent f902e9a commit cf6311b

3 files changed

Lines changed: 118 additions & 94 deletions

File tree

plugins/lookup/secret.py

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# GNU General Public License v3.0+ (see LICENSE-GPL-3 or https://www.gnu.org/licenses/gpl-3.0.txt)
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
14
from __future__ import absolute_import, division, print_function
25

36
__metaclass__ = type
@@ -22,7 +25,8 @@
2225
description:
2326
- Field name to extract from credential.
2427
- Supported fields depend on credential type.
25-
- Common fields include username, password, domain, connectionString, apiId, apiKey, tenantId, clientId, clientSecret, privateKeyData, publicKeyData, privateKeyPassPhrase.
28+
- Common fields include username, password, domain, connectionString, apiId, apiKey, tenantId, clientId, clientSecret, privateKeyData,
29+
publicKeyData, privateKeyPassPhrase.
2630
type: str
2731
default: password
2832
server_base_url:
@@ -43,7 +47,6 @@
4347
- Falls back to DVLS_APP_SECRET environment variable if not provided.
4448
type: str
4549
required: false
46-
no_log: true
4750
vault_id:
4851
description:
4952
- Vault UUID containing the credential.
@@ -53,7 +56,8 @@
5356
notes:
5457
- Requires network access to DVLS server.
5558
- Authentication token is cached for the duration of the playbook run.
56-
- Supported fields include username, password, domain, connectionString, apiId, apiKey, tenantId, clientId, clientSecret, privateKeyData, publicKeyData, privateKeyPassPhrase.
59+
- Supported fields include username, password, domain, connectionString, apiId, apiKey, tenantId, clientId, clientSecret, privateKeyData,
60+
publicKeyData, privateKeyPassPhrase.
5761
"""
5862

5963
EXAMPLES = r"""
@@ -105,11 +109,12 @@
105109
"""
106110

107111
from ansible.errors import AnsibleError
112+
from ansible.plugins.lookup import LookupBase
108113
from ansible.utils.display import Display
109114

110115
try:
111116
from ansible_collections.devolutions.dvls.plugins.module_utils.lookup_base import (
112-
BaseDVLSLookup,
117+
DVLSLookupHelper,
113118
SUPPORTED_CREDENTIAL_FIELDS,
114119
)
115120
except ImportError as e:
@@ -118,10 +123,45 @@
118123
display = Display()
119124

120125

121-
class LookupModule(BaseDVLSLookup):
126+
class LookupModule(LookupBase):
122127
"""Lookup plugin to retrieve a specific field from a DVLS credential."""
123128

124-
def transform_result(self, credential, term):
129+
def __init__(self, *args, **kwargs):
130+
super(LookupModule, self).__init__(*args, **kwargs)
131+
self._helper = DVLSLookupHelper(display, AnsibleError)
132+
133+
def run(self, terms, variables=None, **kwargs):
134+
"""
135+
Main lookup execution method.
136+
137+
Handles authentication, credential retrieval, and result transformation.
138+
"""
139+
self.set_options(var_options=variables, direct=kwargs)
140+
141+
config = self._helper.get_config(self.get_option, variables)
142+
self._helper.authenticate(
143+
config["server_base_url"], config["app_key"], config["app_secret"]
144+
)
145+
146+
results = []
147+
for term in terms:
148+
try:
149+
credential = self._helper.get_credential(
150+
config["server_base_url"], config["vault_id"], term
151+
)
152+
result = self._transform_result(credential, term)
153+
results.append(result)
154+
155+
except AnsibleError:
156+
raise
157+
except Exception as e:
158+
raise AnsibleError(
159+
f"Failed to retrieve credential '{term}': {e}"
160+
) from e
161+
162+
return results
163+
164+
def _transform_result(self, credential, term):
125165
"""
126166
Extract a specific field from the credential.
127167
@@ -136,7 +176,8 @@ def transform_result(self, credential, term):
136176

137177
if field not in SUPPORTED_CREDENTIAL_FIELDS:
138178
raise AnsibleError(
139-
f"Invalid field '{field}'. Supported fields: {', '.join(sorted(SUPPORTED_CREDENTIAL_FIELDS))}"
179+
f"Invalid field '{field}'. "
180+
f"Supported fields: {', '.join(sorted(SUPPORTED_CREDENTIAL_FIELDS))}"
140181
)
141182

142183
field_value = credential.get(field)

plugins/lookup/secret_full.py

Lines changed: 37 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# GNU General Public License v3.0+ (see LICENSE-GPL-3 or https://www.gnu.org/licenses/gpl-3.0.txt)
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
14
from __future__ import absolute_import, division, print_function
25

36
__metaclass__ = type
@@ -37,7 +40,6 @@
3740
- Falls back to DVLS_APP_SECRET environment variable if not provided.
3841
type: str
3942
required: false
40-
no_log: true
4143
vault_id:
4244
description:
4345
- Vault UUID containing the credential.
@@ -109,31 +111,53 @@
109111
"""
110112

111113
from ansible.errors import AnsibleError
114+
from ansible.plugins.lookup import LookupBase
112115
from ansible.utils.display import Display
113116

114117
try:
115118
from ansible_collections.devolutions.dvls.plugins.module_utils.lookup_base import (
116-
BaseDVLSLookup,
119+
DVLSLookupHelper,
117120
)
118121
except ImportError as e:
119122
raise AnsibleError(f"Failed to import DVLS module_utils: {e}")
120123

121124
display = Display()
122125

123126

124-
class LookupModule(BaseDVLSLookup):
127+
class LookupModule(LookupBase):
125128
"""Lookup plugin to retrieve complete DVLS credential objects."""
126129

127-
def transform_result(self, credential, term):
128-
"""
129-
Return the complete credential object.
130+
def __init__(self, *args, **kwargs):
131+
super(LookupModule, self).__init__(*args, **kwargs)
132+
self._helper = DVLSLookupHelper(display, AnsibleError)
130133

131-
Args:
132-
credential: Complete credential object from DVLS
133-
term: Original lookup term
134+
def run(self, terms, variables=None, **kwargs):
135+
"""
136+
Main lookup execution method.
134137
135-
Returns:
136-
dict: The complete credential object
138+
Handles authentication, credential retrieval, and result transformation.
137139
"""
138-
display.vvv(f"Successfully retrieved credential '{term}'")
139-
return credential
140+
self.set_options(var_options=variables, direct=kwargs)
141+
142+
config = self._helper.get_config(self.get_option, variables)
143+
self._helper.authenticate(
144+
config["server_base_url"], config["app_key"], config["app_secret"]
145+
)
146+
147+
results = []
148+
for term in terms:
149+
try:
150+
credential = self._helper.get_credential(
151+
config["server_base_url"], config["vault_id"], term
152+
)
153+
display.vvv(f"Successfully retrieved credential '{term}'")
154+
results.append(credential)
155+
156+
except AnsibleError:
157+
raise
158+
except Exception as e:
159+
raise AnsibleError(
160+
f"Failed to retrieve credential '{term}': {e}"
161+
) from e
162+
163+
return results

plugins/module_utils/lookup_base.py

Lines changed: 33 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,6 @@
66
import os
77
import re
88

9-
from ansible.errors import AnsibleError
10-
from ansible.plugins.lookup import LookupBase
11-
from ansible.utils.display import Display
12-
139
try:
1410
from ansible_collections.devolutions.dvls.plugins.module_utils.auth import (
1511
login,
@@ -19,10 +15,9 @@
1915
get_vault_entry,
2016
get_vault_entry_from_name,
2117
)
22-
except ImportError as e:
23-
raise AnsibleError(f"Failed to import DVLS module_utils: {e}")
24-
25-
display = Display()
18+
except ImportError:
19+
# Will be caught by lookup plugins
20+
pass
2621

2722
UUID_PATTERN = re.compile(
2823
r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$",
@@ -45,39 +40,47 @@
4540
}
4641

4742

48-
class BaseDVLSLookup(LookupBase):
49-
"""Base class for DVLS lookup plugins with shared authentication and retrieval logic."""
43+
class DVLSLookupHelper:
44+
"""Helper class for DVLS lookup plugins with shared authentication and retrieval logic."""
45+
46+
def __init__(self, display_instance, ansible_error_class):
47+
"""
48+
Initialize the helper.
5049
51-
def __init__(self, *args, **kwargs):
52-
super(BaseDVLSLookup, self).__init__(*args, **kwargs)
50+
Args:
51+
display_instance: Display instance for logging
52+
ansible_error_class: AnsibleError class for raising exceptions
53+
"""
5354
self._token = None
5455
self._server_base_url = None
5556
self._cleanup_registered = False
57+
self._display = display_instance
58+
self._ansible_error = ansible_error_class
5659

5760
def _cleanup(self):
5861
"""Cleanup method called at interpreter exit"""
5962
if self._token and self._server_base_url:
6063
try:
61-
display.vvv("Logging out from DVLS")
64+
self._display.vvv("Logging out from DVLS")
6265
logout(self._server_base_url, self._token)
6366
except Exception as e:
64-
display.warning(f"Failed to logout from DVLS during cleanup: {e}")
67+
self._display.warning(f"Failed to logout from DVLS during cleanup: {e}")
6568

6669
def _is_uuid(self, value):
6770
"""Check if a string matches UUID format"""
6871
return UUID_PATTERN.match(value) is not None
6972

70-
def _get_config(self, variables):
73+
def get_config(self, get_option, variables):
7174
"""Extract configuration from options and environment variables"""
72-
server_base_url = self.get_option("server_base_url") or os.environ.get(
75+
server_base_url = get_option("server_base_url") or os.environ.get(
7376
"DVLS_SERVER_BASE_URL"
7477
)
75-
app_key = self.get_option("app_key") or os.environ.get("DVLS_APP_KEY")
76-
app_secret = self.get_option("app_secret") or os.environ.get("DVLS_APP_SECRET")
77-
vault_id = self.get_option("vault_id") or os.environ.get("DVLS_VAULT_ID")
78+
app_key = get_option("app_key") or os.environ.get("DVLS_APP_KEY")
79+
app_secret = get_option("app_secret") or os.environ.get("DVLS_APP_SECRET")
80+
vault_id = get_option("vault_id") or os.environ.get("DVLS_VAULT_ID")
7881

7982
if not all([server_base_url, app_key, app_secret, vault_id]):
80-
raise AnsibleError(
83+
raise self._ansible_error(
8184
"Missing required configuration. Set DVLS_SERVER_BASE_URL, DVLS_APP_KEY, "
8285
"DVLS_APP_SECRET, and DVLS_VAULT_ID environment variables or pass as parameters."
8386
)
@@ -89,21 +92,21 @@ def _get_config(self, variables):
8992
"vault_id": vault_id,
9093
}
9194

92-
def _authenticate(self, server_base_url, app_key, app_secret):
95+
def authenticate(self, server_base_url, app_key, app_secret):
9396
"""Authenticate to DVLS and cache the token"""
9497
if not self._token:
9598
try:
96-
display.vvv(f"Authenticating to DVLS at {server_base_url}")
99+
self._display.vvv(f"Authenticating to DVLS at {server_base_url}")
97100
self._token = login(server_base_url, app_key, app_secret)
98101
self._server_base_url = server_base_url
99102

100103
if not self._cleanup_registered:
101104
atexit.register(self._cleanup)
102105
self._cleanup_registered = True
103106
except Exception as e:
104-
raise AnsibleError(f"DVLS authentication failed: {e}") from e
107+
raise self._ansible_error(f"DVLS authentication failed: {e}") from e
105108

106-
def _get_credential(self, server_base_url, vault_id, term):
109+
def get_credential(self, server_base_url, vault_id, term):
107110
"""
108111
Retrieve a credential from DVLS by name or UUID.
109112
@@ -115,70 +118,26 @@ def _get_credential(self, server_base_url, vault_id, term):
115118
Returns:
116119
dict: Complete credential object
117120
"""
118-
display.vvv(f"Looking up credential: {term}")
121+
self._display.vvv(f"Looking up credential: {term}")
119122

120123
if self._is_uuid(term):
121-
display.vvv(f"Using ID lookup for {term}")
124+
self._display.vvv(f"Using ID lookup for {term}")
122125
response = get_vault_entry(server_base_url, self._token, vault_id, term)
123126
credential = response.get("data", {})
124127
else:
125-
display.vvv(f"Using name lookup for {term}")
128+
self._display.vvv(f"Using name lookup for {term}")
126129
response = get_vault_entry_from_name(
127130
server_base_url, self._token, vault_id, term
128131
)
129132
entries = response.get("data", [])
130133
if not entries:
131-
raise AnsibleError(f"Credential '{term}' not found in vault {vault_id}")
134+
raise self._ansible_error(
135+
f"Credential '{term}' not found in vault {vault_id}"
136+
)
132137
entry_id = entries[0].get("id")
133138
full_response = get_vault_entry(
134139
server_base_url, self._token, vault_id, entry_id
135140
)
136141
credential = full_response.get("data", {})
137142

138143
return credential
139-
140-
def transform_result(self, credential, term):
141-
"""
142-
Transform the credential into the desired output format.
143-
144-
This method must be implemented by subclasses.
145-
146-
Args:
147-
credential: Complete credential object from DVLS
148-
term: Original lookup term
149-
150-
Returns:
151-
The transformed result (field value, full object, etc.)
152-
"""
153-
raise NotImplementedError("Subclasses must implement transform_result()")
154-
155-
def run(self, terms, variables=None, **kwargs):
156-
"""
157-
Main lookup execution method.
158-
159-
Handles authentication, credential retrieval, and result transformation.
160-
"""
161-
self.set_options(var_options=variables, direct=kwargs)
162-
163-
config = self._get_config(variables)
164-
self._authenticate(
165-
config["server_base_url"], config["app_key"], config["app_secret"]
166-
)
167-
168-
results = []
169-
for term in terms:
170-
try:
171-
credential = self._get_credential(
172-
config["server_base_url"], config["vault_id"], term
173-
)
174-
result = self.transform_result(credential, term)
175-
results.append(result)
176-
177-
except AnsibleError:
178-
raise
179-
except Exception as e:
180-
raise AnsibleError(
181-
f"Failed to retrieve credential '{term}': {e}"
182-
) from e
183-
184-
return results

0 commit comments

Comments
 (0)