Skip to content

Commit f581599

Browse files
committed
Move Conjur Cloud regex to module level, more test improvements
1 parent a36319f commit f581599

File tree

4 files changed

+44
-35
lines changed

4 files changed

+44
-35
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99
## [0.1.9] - 2025-12-31
1010

1111
### Added
12-
- Support dry_run parameter for policy load methods. (CNJR-11338)
12+
- Support dry_run parameter for policy load methods, based on [PR #51](https://github.com/cyberark/conjur-api-python/pull/51). (CNJR-11338)
1313

1414
## [0.1.8] - 2025-11-07
1515

conjur_api/client.py

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,25 @@
3030

3131
logger = logging.getLogger(__name__)
3232

33+
# List of possible Secrets Manager SaaS URL suffixes
34+
_CONJUR_CLOUD_SUFFIXES = [
35+
".cyberark.cloud",
36+
".integration-cyberark.cloud",
37+
".test-cyberark.cloud",
38+
".dev-cyberark.cloud",
39+
".cyberark-everest-integdev.cloud",
40+
".cyberark-everest-pre-prod.cloud",
41+
".sandbox-cyberark.cloud",
42+
".pt-cyberark.cloud",
43+
]
44+
45+
# Build regex pattern: (\\.secretsmgr|-secretsmanager) followed by one of the suffixes
46+
_SUFFIXES_PATTERN = "|".join(re.escape(suffix) for suffix in _CONJUR_CLOUD_SUFFIXES)
47+
_CONJUR_CLOUD_REGEXP = re.compile(
48+
rf"(\.secretsmgr|-secretsmanager)({_SUFFIXES_PATTERN})",
49+
re.IGNORECASE
50+
)
51+
3352
@allow_sync_invocation()
3453
# pylint: disable=too-many-public-methods
3554
class Client:
@@ -42,7 +61,8 @@ class Client:
4261
try:
4362
integration_version = version("conjur_api")
4463
except PackageNotFoundError:
45-
integration_version = '0.0.dev'
64+
# setuptools defaults to "0.0.dev0" (PEP 440), so we use a default version that adheres to that for testing purposes
65+
integration_version = '0.0.dev0'
4666
integration_type = 'cybr-secretsmanager'
4767
vendor_name = 'CyberArk'
4868
vendor_version = None
@@ -379,24 +399,7 @@ def _is_conjur_cloud_url(self, url: str) -> bool:
379399
if not url:
380400
return False
381401

382-
# ConjurCloudSuffixes - all possible Secrets Manager SaaS URL suffixes
383-
conjur_cloud_suffixes = [
384-
".cyberark.cloud",
385-
".integration-cyberark.cloud",
386-
".test-cyberark.cloud",
387-
".dev-cyberark.cloud",
388-
".cyberark-everest-integdev.cloud",
389-
".cyberark-everest-pre-prod.cloud",
390-
".sandbox-cyberark.cloud",
391-
".pt-cyberark.cloud",
392-
]
393-
394-
# Build regex pattern: (\\.secretsmgr|-secretsmanager) followed by one of the suffixes
395-
suffixes_pattern = "|".join(re.escape(suffix) for suffix in conjur_cloud_suffixes)
396-
pattern = rf"(\.secretsmgr|-secretsmanager)({suffixes_pattern})"
397-
398-
conjur_cloud_regexp = re.compile(pattern, re.IGNORECASE)
399-
return bool(conjur_cloud_regexp.search(url))
402+
return bool(_CONJUR_CLOUD_REGEXP.search(url))
400403

401404
async def server_version(self) -> str:
402405
"""

tests/https/test_unit_client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,12 @@ def test_is_version_less_than_less_than(self):
503503
self.assertTrue(self.client._is_version_less_than("1.20.1", "1.21.1"))
504504
self.assertTrue(self.client._is_version_less_than("1.21.1", "2.0.0"))
505505

506+
def test_is_version_less_than_with_suffix(self):
507+
"""Test version comparison with build suffixes"""
508+
self.assertTrue(self.client._is_version_less_than("1.21.0-12345", "1.21.1"))
509+
self.assertFalse(self.client._is_version_less_than("1.21.1-12345", "1.21.1"))
510+
self.assertFalse(self.client._is_version_less_than("1.21.2-12345", "1.21.1"))
511+
506512
def test_is_conjur_cloud_url_secretsmgr(self):
507513
"""Test Conjur Cloud URL detection with .secretsmgr pattern"""
508514
self.assertTrue(self.client._is_conjur_cloud_url("https://example.secretsmgr.cyberark.cloud"))

tests/https/test_unit_http.py

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ async def test_invoke_endpoint_can_invoke_http_client(self, mock_request):
5656
await invoke_endpoint(HttpVerb.GET, self.MockEndpoint.NO_PARAMS, {})
5757

5858
mock_create_ssl_context.assert_called_once_with()
59-
mock_request.assert_called_once_with('GET', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, data='', ssl=ssl_context,
59+
mock_request.assert_called_once_with('GET', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, data='', ssl=ssl_context,
6060
params=None, proxy=None)
6161

6262
@patch('aiohttp.ClientSession.request')
@@ -67,7 +67,7 @@ async def test_invoke_endpoint_can_handle_unset_params(self, mock_request):
6767
await invoke_endpoint(HttpVerb.GET, self.MockEndpoint.NO_PARAMS, None)
6868

6969
mock_create_ssl_context.assert_called_once_with()
70-
mock_request.assert_called_once_with('GET', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, data='', ssl=ssl_context,
70+
mock_request.assert_called_once_with('GET', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, data='', ssl=ssl_context,
7171
params=None, proxy=None)
7272

7373
@patch('aiohttp.ClientSession.request')
@@ -77,15 +77,15 @@ async def test_invoke_endpoint_uses_http_verb_for_method_name(self, mock_request
7777
mock_request.return_value = MockResponse('', 200)
7878
await invoke_endpoint(HttpVerb.GET, self.MockEndpoint.NO_PARAMS, {})
7979
mock_create_ssl_context.assert_called_with()
80-
mock_request.assert_called_with('GET', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, data='', ssl=ssl_context,
80+
mock_request.assert_called_with('GET', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, data='', ssl=ssl_context,
8181
params=None, proxy=None)
8282

8383
await invoke_endpoint(HttpVerb.POST, self.MockEndpoint.NO_PARAMS, {})
84-
mock_request.assert_called_with('POST', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, data='', ssl=ssl_context,
84+
mock_request.assert_called_with('POST', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, data='', ssl=ssl_context,
8585
params=None, proxy=None)
8686

8787
await invoke_endpoint(HttpVerb.DELETE, self.MockEndpoint.NO_PARAMS, {})
88-
mock_request.assert_called_with('DELETE', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, data='', ssl=ssl_context,
88+
mock_request.assert_called_with('DELETE', 'no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, data='', ssl=ssl_context,
8989
params=None, proxy=None)
9090

9191
@patch('aiohttp.ClientSession.request')
@@ -96,7 +96,7 @@ async def test_invoke_endpoint_generates_url_from_endpoint_object(self, mock_req
9696
await invoke_endpoint(HttpVerb.GET, self.MockEndpoint.WITH_URL, {'url': 'http://host'})
9797

9898
mock_create_ssl_context.assert_called_once_with()
99-
mock_request.assert_called_once_with('GET', 'http://host/no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, data='',
99+
mock_request.assert_called_once_with('GET', 'http://host/no/params', auth=None, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, data='',
100100
ssl=ssl_context, params=None, proxy=None)
101101

102102
@patch('aiohttp.ClientSession.request')
@@ -108,7 +108,7 @@ async def test_invoke_endpoint_attaches_api_token_header_if_present_in_params(se
108108

109109
mock_create_ssl_context.assert_called_once_with()
110110
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='', ssl=ssl_context,
111-
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw','Authorization': 'Token token="dG9rZW4="'}, params=None,
111+
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms','Authorization': 'Token token="dG9rZW4="'}, params=None,
112112
proxy=None)
113113

114114
@patch('aiohttp.ClientSession.request')
@@ -119,7 +119,7 @@ async def test_invoke_endpoint_verifies_ssl_by_default(self, mock_request):
119119
await invoke_endpoint(HttpVerb.GET, self.MockEndpoint.NO_PARAMS, None)
120120

121121
mock_create_ssl_context.assert_called_once_with()
122-
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='', ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'},
122+
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='', ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'},
123123
params=None, proxy=None)
124124

125125
@patch('aiohttp.ClientSession.request')
@@ -131,7 +131,7 @@ async def test_invoke_endpoint_ssl_verify_param_defaults_to_true_to_http_client(
131131
ssl_verification_metadata=create_ssl_verification_metadata())
132132

133133
mock_create_ssl_context.assert_called_once_with()
134-
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='', ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'},
134+
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='', ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'},
135135
params=None, proxy=None)
136136

137137
@patch('aiohttp.ClientSession.request')
@@ -148,7 +148,7 @@ async def test_invoke_endpoint_raises_hostname_mismatch_error(self, mock_request
148148

149149
ssl_context_calls = [call(cafile='foo')]
150150
mock_create_ssl_context.assert_has_calls(ssl_context_calls)
151-
mock_request.assert_called_with('GET', 'no/params', auth=None, data='', ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'},
151+
mock_request.assert_called_with('GET', 'no/params', auth=None, data='', ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'},
152152
params=None, proxy=None)
153153

154154
@patch('aiohttp.ClientSession.request')
@@ -160,7 +160,7 @@ async def test_invoke_endpoint_passes_auth_param_to_http_client_if_provided(self
160160

161161
mock_create_ssl_context.assert_called_once_with()
162162
mock_request.assert_called_once_with('GET', 'no/params', auth=BasicAuth('foo', 'bar'), data='',
163-
ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, params=None, proxy=None)
163+
ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, params=None, proxy=None)
164164

165165
@patch('aiohttp.ClientSession.request')
166166
async def test_invoke_endpoint_passes_extra_args_to_http_client(self, mock_request):
@@ -171,7 +171,7 @@ async def test_invoke_endpoint_passes_extra_args_to_http_client(self, mock_reque
171171

172172
mock_create_ssl_context.assert_called_once_with()
173173
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='ab', ssl=ssl_context,
174-
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, params=None, proxy=None)
174+
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, params=None, proxy=None)
175175

176176
@patch('aiohttp.ClientSession.request')
177177
async def test_invoke_endpoint_passes_query_param(self, mock_request):
@@ -186,7 +186,7 @@ async def test_invoke_endpoint_passes_query_param(self, mock_request):
186186

187187
mock_create_ssl_context.assert_called_once_with()
188188
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='ab', ssl=ssl_context,
189-
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, params=query, proxy=None)
189+
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, params=query, proxy=None)
190190

191191
@patch('aiohttp.ClientSession.request')
192192
async def test_invoke_endpoint_passes_proxy_param(self, mock_request):
@@ -198,7 +198,7 @@ async def test_invoke_endpoint_passes_proxy_param(self, mock_request):
198198

199199
mock_create_ssl_context.assert_called_once_with()
200200
mock_request.assert_called_once_with('GET', 'no/params', auth=None, data='ab', ssl=ssl_context,
201-
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, params=None, proxy='proxy.com')
201+
headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, params=None, proxy='proxy.com')
202202

203203
@patch('aiohttp.ClientSession.request')
204204
async def test_invoke_endpoint_quotes_all_params_except_url(self, mock_request):
@@ -211,7 +211,7 @@ async def test_invoke_endpoint_quotes_all_params_except_url(self, mock_request):
211211
quoted_endpoint = '/'.join([self.UNESCAPED_PARAMS['url']] + self.ESCAPED_PARAMS)
212212
mock_create_ssl_context.assert_called_once_with()
213213
mock_request.assert_called_once_with('GET', quoted_endpoint, data='$#\\% ^%', auth=None,
214-
ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldiZ2bj1DeWJlckFyaw'}, params=None, proxy=None)
214+
ssl=ssl_context, headers={'x-cybr-telemetry': 'aW49U2VjcmV0c01hbmFnZXJQeXRob24gU0RLJml0PWN5YnItc2VjcmV0c21hbmFnZXImaXY9MC4wLmRldjAmdm49Q3liZXJBcms'}, params=None, proxy=None)
215215

216216
@patch('aiohttp.ClientSession.request')
217217
async def test_invoke_endpoint_raises_error_if_bad_status_code_is_returned(self, mock_request):

0 commit comments

Comments
 (0)