ark-sdk-python 1.0.4__py3-none-any.whl → 1.0.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,6 +16,7 @@ from ark_sdk_python.models.auth import (
16
16
  ArkAuthMethodsRequireCredentials,
17
17
  ArkSecret,
18
18
  ArkToken,
19
+ IdentityArkAuthMethodSettings,
19
20
  )
20
21
 
21
22
 
@@ -138,7 +139,19 @@ class ArkLoginAction(ArkAction):
138
139
  if (
139
140
  authenticator_name == 'isp'
140
141
  and auth_profile.auth_method == ArkAuthMethod.Identity
141
- and ArkIdentity.is_idp_user(auth_profile.username)
142
+ and ArkIdentity.is_idp_user(
143
+ auth_profile.username,
144
+ (
145
+ auth_profile.auth_method_settings.identity_url
146
+ if isinstance(auth_profile.auth_method_settings, IdentityArkAuthMethodSettings)
147
+ else None
148
+ ),
149
+ (
150
+ auth_profile.auth_method_settings.identity_tenant_subdomain
151
+ if isinstance(auth_profile.auth_method_settings, IdentityArkAuthMethodSettings)
152
+ else None
153
+ ),
154
+ )
142
155
  ):
143
156
  secret = ArkSecret(secret='')
144
157
  else:
ark_sdk_python/ark.py CHANGED
@@ -17,7 +17,7 @@ from ark_sdk_python.actions import ArkAction, ArkCacheAction, ArkConfigureAction
17
17
 
18
18
  urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
19
19
 
20
- __version__ = '1.0.4'
20
+ __version__ = '1.0.6'
21
21
 
22
22
 
23
23
  def main():
@@ -46,6 +46,7 @@ class ArkISPAuth(ArkAuth):
46
46
  username=auth_profile.username,
47
47
  password=secret.secret.get_secret_value() if secret else None,
48
48
  identity_url=method_settings.identity_url,
49
+ identity_tenant_subdomain=method_settings.identity_tenant_subdomain,
49
50
  mfa_type=method_settings.identity_mfa_method,
50
51
  logger=self._logger,
51
52
  cache_authentication=self._cache_authentication,
@@ -1,6 +1,7 @@
1
1
  import codecs
2
2
  import json
3
3
  import logging
4
+ import os
4
5
  import re
5
6
  import sys
6
7
  import time
@@ -19,6 +20,7 @@ from requests import Session
19
20
  from ark_sdk_python.args import ArkArgsFormatter, ArkInquirerRender
20
21
  from ark_sdk_python.auth.identity.ark_identity_fqdn_resolver import ArkIdentityFQDNResolver
21
22
  from ark_sdk_python.common import ArkKeyring, ArkSystemConfig, get_logger
23
+ from ark_sdk_python.common.env import AwsEnv
22
24
  from ark_sdk_python.models import ArkException, ArkNonInteractiveException
23
25
  from ark_sdk_python.models.ark_exceptions import ArkAuthException
24
26
  from ark_sdk_python.models.ark_profile import ArkProfile
@@ -75,6 +77,7 @@ class ArkIdentity:
75
77
  username: str,
76
78
  password: Optional[str],
77
79
  identity_url: Optional[str] = None,
80
+ identity_tenant_subdomain: Optional[str] = None,
78
81
  mfa_type: Optional[str] = None,
79
82
  logger: Optional[logging.Logger] = None,
80
83
  cache_authentication: bool = True,
@@ -84,7 +87,7 @@ class ArkIdentity:
84
87
  ) -> None:
85
88
  self.__username = username
86
89
  self.__password = password
87
- self.__identity_url = identity_url or self.__resolve_fqdn_from_username()
90
+ self.__identity_url = self.__resolve_fqdn_from_username_or_subdomain(identity_url, identity_tenant_subdomain)
88
91
  if not self.__identity_url.startswith('https://'):
89
92
  self.__identity_url = f'https://{self.__identity_url}'
90
93
  self.__mfa_type = mfa_type
@@ -158,7 +161,16 @@ class ArkIdentity:
158
161
  f'{self.__username}_identity_session',
159
162
  )
160
163
 
161
- def __resolve_fqdn_from_username(self) -> str:
164
+ def __resolve_fqdn_from_username_or_subdomain(self, identity_url: Optional[str], identity_tenant_subdomain: Optional[str]) -> str:
165
+ if identity_tenant_subdomain and not identity_url:
166
+ try:
167
+ identity_url = ArkIdentityFQDNResolver.resolve_tenant_fqdn_from_tenant_subdomain(
168
+ identity_tenant_subdomain, AwsEnv(os.environ.get('DEPLOY_ENV', AwsEnv.PROD.value))
169
+ )
170
+ except Exception as ex:
171
+ self.__logger.warning(f'Failed to resolve url from tenant subdomain, falling back to user [{str(ex)}]')
172
+ if identity_url:
173
+ return identity_url
162
174
  tenant_suffix = self.__username[self.__username.index('@') :]
163
175
  return ArkIdentityFQDNResolver.resolve_tenant_fqdn_from_tenant_suffix(tenant_suffix=tenant_suffix)
164
176
 
@@ -166,7 +178,7 @@ class ArkIdentity:
166
178
  self.__logger.info(f'Starting authentication with user {self.__username} and fqdn {self.__identity_url}')
167
179
  response = self.__session.post(
168
180
  url=f'{self.__identity_url}/Security/StartAuthentication',
169
- json={'User': self.__username, 'Version': '1.0', 'PlatformTokenResponse': True},
181
+ json={'User': self.__username, 'Version': '1.0', 'PlatformTokenResponse': True, 'MfaRequestor': 'DeviceAgent'},
170
182
  )
171
183
  try:
172
184
  parsed_res: StartAuthResponse = StartAuthResponse.parse_raw(response.text)
@@ -381,19 +393,26 @@ class ArkIdentity:
381
393
 
382
394
  @classmethod
383
395
  @cached(cache=LRUCache(maxsize=1024))
384
- def is_idp_user(cls, username: str) -> bool:
396
+ def is_idp_user(cls, username: str, identity_url: Optional[str], identity_tenant_subdomain: Optional[str]) -> bool:
385
397
  """
386
398
  Checks whether or not the specified username is from an external IDP.
387
399
 
388
400
  Args:
389
401
  username (str): _description_
402
+ identity_url (Optional[str]): _description_
403
+ identity_tenant_subdomain (Optional[str]): _description_
390
404
 
391
405
  Returns:
392
406
  bool: _description_
393
407
  """
394
408
  if re.match('.*@cyberark\\.cloud\\.(\\d)+', username) is not None:
395
409
  return False
396
- identity = ArkIdentity(username=username, password='')
410
+ identity = ArkIdentity(
411
+ username=username,
412
+ password='',
413
+ identity_url=identity_url,
414
+ identity_tenant_subdomain=identity_tenant_subdomain,
415
+ )
397
416
  resp = identity.__start_authentication()
398
417
  return resp.result.idp_redirect_url != None
399
418
 
@@ -31,6 +31,7 @@ CONFIGURATION_AUTHENTICATOR_IGNORED_INTERACTIVE_KEYS: Final[Dict[str, List[str]]
31
31
  }
32
32
  CONFIGURATION_ALLOWED_EMPTY_VALUES: Final[List[str]] = {
33
33
  'isp_identity_url',
34
+ 'isp_identity_tenant_subdomain',
34
35
  }
35
36
  CONFIGURATION_AUTHENTICATORS_DEFAULTS: Final[Dict[str, str]] = {}
36
37
  CONFIGURATION_OVERRIDE_ALIASES: Final[Dict[str, str]] = {'region': 'Region', 'env': 'Environment'}
@@ -28,6 +28,12 @@ class IdentityArkAuthMethodSettings(ArkAuthMethodSettings):
28
28
  identity_url: Optional[str] = Field(
29
29
  description='Identity url to use for authentication instead of fqdn resolving', alias='Identity Url'
30
30
  )
31
+ identity_tenant_subdomain: Optional[str] = Field(
32
+ description='Identity security platform tenant subdomain, '
33
+ 'for exmaple mytenant.cyberark.cloud would be subdomained to mytenant. '
34
+ 'this will be used instead of fqdn resolving from the username',
35
+ alias='Identity Tenant Subdomain',
36
+ )
31
37
 
32
38
 
33
39
  class IdentityServiceUserArkAuthMethodSettings(ArkAuthMethodSettings):
@@ -66,9 +66,16 @@ class ArkDPASSOService(ArkService):
66
66
  postfix=f'{claims["tenant_id"]}_{claims["unique_name"]}_dpa_sso_short_lived_{token_type}',
67
67
  )
68
68
 
69
+ def __expand_folder(self, folder: str) -> str:
70
+ folder_path = os.path.expanduser(folder)
71
+ if not folder_path.endswith('/'):
72
+ folder_path += '/'
73
+ return folder_path
74
+
69
75
  def __output_client_certificate(
70
76
  self, folder: str, output_format: ArkDPASSOShortLiveClientCertificateFormat, result: ArkDPASSOAcquireTokenResponse
71
77
  ) -> None:
78
+ folder_path = self.__expand_folder(folder)
72
79
  claims = get_unverified_claims(self.__client.session_token)
73
80
  client_certificate = result.token['client_certificate']
74
81
  private_key = result.token['private_key']
@@ -82,24 +89,24 @@ class ArkDPASSOService(ArkService):
82
89
  )
83
90
  ArkArgsFormatter.print_normal(f'client-key-data: {base64.b64encode(private_key.encode("utf-8")).decode("utf-8")}')
84
91
  elif output_format == ArkDPASSOShortLiveClientCertificateFormat.FILE:
85
- if not folder:
92
+ if not folder_path:
86
93
  raise ArkServiceException(
87
94
  f'Folder parameter is required if format is {ArkDPASSOShortLiveClientCertificateFormat.FILE.value}'
88
95
  )
89
- if not os.path.exists(folder):
90
- os.makedirs(folder)
91
- with open(f'{folder}{os.path.sep}{claims["unique_name"]}client_cert.crt', 'w', encoding='utf-8') as file_handle:
96
+ if not os.path.exists(folder_path):
97
+ os.makedirs(folder_path)
98
+ with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}client_cert.crt', 'w', encoding='utf-8') as file_handle:
92
99
  file_handle.write(client_certificate)
93
- with open(f'{folder}{os.path.sep}{claims["unique_name"]}client_key.pem', 'w', encoding='utf-8') as file_handle:
100
+ with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}client_key.pem', 'w', encoding='utf-8') as file_handle:
94
101
  file_handle.write(private_key)
95
102
  elif output_format == ArkDPASSOShortLiveClientCertificateFormat.SINGLE_FILE:
96
103
  if not folder:
97
104
  raise ArkServiceException(
98
105
  f'Folder parameter is required if format is {ArkDPASSOShortLiveClientCertificateFormat.FILE.value}'
99
106
  )
100
- if not os.path.exists(folder):
101
- os.makedirs(folder)
102
- with open(f'{folder}{os.path.sep}{claims["unique_name"]}client_cert.pem', 'w', encoding='utf-8') as file_handle:
107
+ if not os.path.exists(folder_path):
108
+ os.makedirs(folder_path)
109
+ with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}client_cert.pem', 'w', encoding='utf-8') as file_handle:
103
110
  file_handle.write(client_certificate)
104
111
  file_handle.write('\n')
105
112
  file_handle.write(private_key)
@@ -107,32 +114,35 @@ class ArkDPASSOService(ArkService):
107
114
  raise ArkServiceException(f'Unknown format {output_format}')
108
115
 
109
116
  def __save_oracle_sso_wallet(self, folder: str, unzip_wallet: bool, result: ArkDPASSOAcquireTokenResponse) -> None:
117
+ folder_path = self.__expand_folder(folder)
110
118
  result.token['wallet'] = base64.b64decode(result.token['wallet'])
111
- if not os.path.exists(folder):
112
- os.makedirs(folder)
119
+ if not os.path.exists(folder_path):
120
+ os.makedirs(folder_path)
113
121
  if not unzip_wallet:
114
- with open(f'{folder}{os.path.sep}wallet.zip', 'wb') as file_handle:
122
+ with open(f'{folder_path}{os.path.sep}wallet.zip', 'wb') as file_handle:
115
123
  file_handle.write(result.token['wallet'])
116
124
  else:
117
125
  wallet_bytes = BytesIO(result.token['wallet'])
118
126
  with zipfile.ZipFile(wallet_bytes, 'r') as zipf:
119
- zipf.extractall(folder)
127
+ zipf.extractall(folder_path)
120
128
 
121
129
  def __save_oracle_pem_wallet(self, folder: str, result: ArkDPASSOAcquireTokenResponse) -> None:
130
+ folder_path = self.__expand_folder(folder)
122
131
  claims = get_unverified_claims(self.__client.session_token)
123
132
  pem_wallet = base64.b64decode(result.token['pem_wallet']).decode('utf-8')
124
- if not os.path.exists(folder):
125
- os.makedirs(folder)
126
- with open(f'{folder}{os.path.sep}{claims["unique_name"]}ewallet.pem', 'w', encoding='utf-8') as file_handle:
133
+ if not os.path.exists(folder_path):
134
+ os.makedirs(folder_path)
135
+ with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}ewallet.pem', 'w', encoding='utf-8') as file_handle:
127
136
  file_handle.write(pem_wallet)
128
137
 
129
138
  def __save_rdp_file(self, get_short_lived_rdp_file: ArkDPASSOGetShortLivedRDPFile, result: ArkDPASSOAcquireTokenResponse) -> None:
130
- if not os.path.exists(get_short_lived_rdp_file.folder):
131
- os.makedirs(get_short_lived_rdp_file.folder)
139
+ folder_path = self.__expand_folder(get_short_lived_rdp_file.folder)
140
+ if not os.path.exists(folder_path):
141
+ os.makedirs(folder_path)
132
142
  filename: str = f'dpa _a {get_short_lived_rdp_file.target_address}'
133
143
  if get_short_lived_rdp_file.target_domain:
134
144
  filename += f' _d {get_short_lived_rdp_file.target_domain}'
135
- with open(f'{get_short_lived_rdp_file.folder}{filename}.rdp', 'w', encoding='utf-8') as file_handle:
145
+ with open(f'{folder_path}{filename}.rdp', 'w', encoding='utf-8') as file_handle:
136
146
  file_handle.write(result.token['text'])
137
147
 
138
148
  def short_lived_password(self, get_short_lived_password: ArkDPASSOGetShortLivedPassword) -> str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ark-sdk-python
3
- Version: 1.0.4
3
+ Version: 1.0.6
4
4
  Summary: Official Ark SDK / CLI for CyberArk Identity Security Platform
5
5
  Home-page: https://github.com/cyberark/ark-sdk-python
6
6
  License: Apache-2.0