Skip to content

Commit 207043b

Browse files
authored
ADB-600: Fixed an issue where the idp user check was not taking the identity url into account, also added subdomain optional param for discovery (cyberark#17)
1 parent 971b6d9 commit 207043b

File tree

7 files changed

+49
-9
lines changed

7 files changed

+49
-9
lines changed

ark_sdk_python/actions/ark_login_action.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
ArkAuthMethodsRequireCredentials,
1717
ArkSecret,
1818
ArkToken,
19+
IdentityArkAuthMethodSettings,
1920
)
2021

2122

@@ -138,7 +139,19 @@ def run_action(self, args: argparse.Namespace) -> None:
138139
if (
139140
authenticator_name == 'isp'
140141
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+
)
142155
):
143156
secret = ArkSecret(secret='')
144157
else:

ark_sdk_python/auth/ark_isp_auth.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ def __perform_identity_authentication(
4646
username=auth_profile.username,
4747
password=secret.secret.get_secret_value() if secret else None,
4848
identity_url=method_settings.identity_url,
49+
identity_tenant_subdomain=method_settings.identity_tenant_subdomain,
4950
mfa_type=method_settings.identity_mfa_method,
5051
logger=self._logger,
5152
cache_authentication=self._cache_authentication,

ark_sdk_python/auth/identity/ark_identity.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import codecs
22
import json
33
import logging
4+
import os
45
import re
56
import sys
67
import time
@@ -19,6 +20,7 @@
1920
from ark_sdk_python.args import ArkArgsFormatter, ArkInquirerRender
2021
from ark_sdk_python.auth.identity.ark_identity_fqdn_resolver import ArkIdentityFQDNResolver
2122
from ark_sdk_python.common import ArkKeyring, ArkSystemConfig, get_logger
23+
from ark_sdk_python.common.env import AwsEnv
2224
from ark_sdk_python.models import ArkException, ArkNonInteractiveException
2325
from ark_sdk_python.models.ark_exceptions import ArkAuthException
2426
from ark_sdk_python.models.ark_profile import ArkProfile
@@ -75,6 +77,7 @@ def __init__(
7577
username: str,
7678
password: Optional[str],
7779
identity_url: Optional[str] = None,
80+
identity_tenant_subdomain: Optional[str] = None,
7881
mfa_type: Optional[str] = None,
7982
logger: Optional[logging.Logger] = None,
8083
cache_authentication: bool = True,
@@ -84,7 +87,7 @@ def __init__(
8487
) -> None:
8588
self.__username = username
8689
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)
8891
if not self.__identity_url.startswith('https://'):
8992
self.__identity_url = f'https://{self.__identity_url}'
9093
self.__mfa_type = mfa_type
@@ -158,15 +161,24 @@ def __save_cache(self, profile: Optional[ArkProfile] = None) -> None:
158161
f'{self.__username}_identity_session',
159162
)
160163

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
162174
tenant_suffix = self.__username[self.__username.index('@') :]
163175
return ArkIdentityFQDNResolver.resolve_tenant_fqdn_from_tenant_suffix(tenant_suffix=tenant_suffix)
164176

165177
def __start_authentication(self) -> StartAuthResponse:
166178
self.__logger.info(f'Starting authentication with user {self.__username} and fqdn {self.__identity_url}')
167179
response = self.__session.post(
168180
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'},
170182
)
171183
try:
172184
parsed_res: StartAuthResponse = StartAuthResponse.parse_raw(response.text)
@@ -381,19 +393,26 @@ def has_cache_record(cls, profile: ArkProfile, username: str, refresh_auth_allow
381393

382394
@classmethod
383395
@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:
385397
"""
386398
Checks whether or not the specified username is from an external IDP.
387399
388400
Args:
389401
username (str): _description_
402+
identity_url (Optional[str]): _description_
403+
identity_tenant_subdomain (Optional[str]): _description_
390404
391405
Returns:
392406
bool: _description_
393407
"""
394408
if re.match('.*@cyberark\\.cloud\\.(\\d)+', username) is not None:
395409
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+
)
397416
resp = identity.__start_authentication()
398417
return resp.result.idp_redirect_url != None
399418

ark_sdk_python/models/actions/ark_configure_action_consts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
}
3232
CONFIGURATION_ALLOWED_EMPTY_VALUES: Final[List[str]] = {
3333
'isp_identity_url',
34+
'isp_identity_tenant_subdomain',
3435
}
3536
CONFIGURATION_AUTHENTICATORS_DEFAULTS: Final[Dict[str, str]] = {}
3637
CONFIGURATION_OVERRIDE_ALIASES: Final[Dict[str, str]] = {'region': 'Region', 'env': 'Environment'}

ark_sdk_python/models/auth/ark_auth_method.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ class IdentityArkAuthMethodSettings(ArkAuthMethodSettings):
2828
identity_url: Optional[str] = Field(
2929
description='Identity url to use for authentication instead of fqdn resolving', alias='Identity Url'
3030
)
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+
)
3137

3238

3339
class IdentityServiceUserArkAuthMethodSettings(ArkAuthMethodSettings):

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tool.poetry]
22
name = "ark-sdk-python"
3-
version = "1.0.4"
3+
version = "1.0.5"
44
description='Official Ark SDK / CLI for CyberArk Identity Security Platform'
55
authors = ["CyberArk <[email protected]>", "Ofir Iluz <[email protected]"]
66
readme = "README.md"

tests/unit/auth/test_ark_isp_auth.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def test_identity_auth_method(self, mocker: MockerFixture, auth_method: ArkAuthM
4848
tenant_fqdn_mock.assert_called_once()
4949
session_mock.return_value.post.assert_any_call(
5050
url='https://url.com/Security/StartAuthentication',
51-
json={'User': '[email protected]', 'Version': '1.0', 'PlatformTokenResponse': True},
51+
json={'User': '[email protected]', 'Version': '1.0', 'PlatformTokenResponse': True, 'MfaRequestor': 'DeviceAgent'},
5252
)
5353
session_mock.return_value.post.assert_any_call(
5454
url='https://url.com/Security/AdvanceAuthentication',
@@ -129,7 +129,7 @@ def test_identity_auth_method_caching(self, mocker: MockerFixture):
129129
pickle_dumps_mock.assert_called()
130130
session_mock.return_value.post.assert_any_call(
131131
url='https://url.com/Security/StartAuthentication',
132-
json={'User': '[email protected]', 'Version': '1.0', 'PlatformTokenResponse': True},
132+
json={'User': '[email protected]', 'Version': '1.0', 'PlatformTokenResponse': True, 'MfaRequestor': 'DeviceAgent'},
133133
)
134134
session_mock.return_value.post.assert_any_call(
135135
url='https://url.com/Security/AdvanceAuthentication',

0 commit comments

Comments
 (0)