From 8fc69b20f37f03117c5ba639ec9576cca344e47e Mon Sep 17 00:00:00 2001 From: Ofir Iluz Date: Thu, 4 Apr 2024 09:51:54 +0300 Subject: [PATCH 1/6] Identity service support, containing Users, Roles, Policies and Directories API's with fitting validation models --- README.md | 10 + ark_sdk_python/ark_api.py | 48 +++ ark_sdk_python/examples/default_suffix.py | 12 + .../models/actions/services/__init__.py | 3 + .../ark_identity_exec_action_consts.py | 114 +++++ .../models/services/identity/__init__.py | 0 .../services/identity/directories/__init__.py | 21 + .../directories/ark_identity_directory.py | 9 + .../directories/ark_identity_entity.py | 37 ++ .../ark_identity_list_directories.py | 10 + .../ark_identity_list_directories_entities.py | 22 + .../services/identity/policies/__init__.py | 31 ++ ...ark_identity_add_authentication_profile.py | 13 + .../policies/ark_identity_add_policy.py | 13 + .../ark_identity_authentication_profile.py | 14 + .../policies/ark_identity_disable_policy.py | 7 + .../policies/ark_identity_enable_policy.py | 7 + ...ark_identity_get_authentication_profile.py | 10 + .../policies/ark_identity_get_policy.py | 7 + .../identity/policies/ark_identity_policy.py | 25 ++ .../policies/ark_identity_policy_info.py | 11 + .../policies/ark_identity_policy_operation.py | 9 + .../ark_identity_policy_operation_type.py | 6 + ..._identity_remove_authentication_profile.py | 10 + .../policies/ark_identity_remove_policy.py | 7 + .../services/identity/roles/__init__.py | 33 ++ .../ark_identity_add_admin_right_to_role.py | 12 + .../roles/ark_identity_add_group_to_role.py | 8 + .../roles/ark_identity_add_role_to_role.py | 8 + .../roles/ark_identity_add_user_to_role.py | 8 + .../roles/ark_identity_admin_right.py | 10 + .../roles/ark_identity_create_role.py | 12 + .../roles/ark_identity_delete_role.py | 10 + .../roles/ark_identity_list_role_members.py | 10 + .../ark_identity_remove_group_from_role.py | 8 + .../ark_identity_remove_role_from_role.py | 8 + .../ark_identity_remove_user_from_role.py | 8 + .../identity/roles/ark_identity_role.py | 8 + .../roles/ark_identity_role_id_by_name.py | 7 + .../roles/ark_identity_role_member.py | 10 + .../roles/ark_identity_update_role.py | 12 + .../services/identity/users/__init__.py | 19 + .../users/ark_identity_create_user.py | 29 ++ .../users/ark_identity_delete_user.py | 10 + .../users/ark_identity_reset_user_password.py | 8 + .../users/ark_identity_update_user.py | 14 + .../identity/users/ark_identity_user.py | 16 + .../users/ark_identity_user_by_name.py | 7 + .../users/ark_identity_user_id_by_name.py | 7 + .../identity/users/ark_identity_user_info.py | 16 + ark_sdk_python/services/identity/__init__.py | 3 + .../services/identity/ark_identity_api.py | 53 +++ .../services/identity/common/__init__.py | 3 + .../common/ark_identity_base_service.py | 38 ++ .../services/identity/directories/__init__.py | 3 + .../ark_identity_directories_service.py | 216 ++++++++++ .../services/identity/policies/__init__.py | 3 + .../policies/ark_identity_policies_service.py | 366 ++++++++++++++++ .../services/identity/roles/__init__.py | 3 + .../roles/ark_identity_roles_service.py | 402 ++++++++++++++++++ .../services/identity/users/__init__.py | 3 + .../users/ark_identity_users_service.py | 267 ++++++++++++ docs/commands/exec.md | 12 +- docs/examples/commands_examples.md | 40 ++ docs/examples/sdk_examples.md | 147 ++++--- pyproject.toml | 2 +- 66 files changed, 2246 insertions(+), 69 deletions(-) create mode 100644 ark_sdk_python/examples/default_suffix.py create mode 100644 ark_sdk_python/models/actions/services/ark_identity_exec_action_consts.py create mode 100644 ark_sdk_python/models/services/identity/__init__.py create mode 100644 ark_sdk_python/models/services/identity/directories/__init__.py create mode 100644 ark_sdk_python/models/services/identity/directories/ark_identity_directory.py create mode 100644 ark_sdk_python/models/services/identity/directories/ark_identity_entity.py create mode 100644 ark_sdk_python/models/services/identity/directories/ark_identity_list_directories.py create mode 100644 ark_sdk_python/models/services/identity/directories/ark_identity_list_directories_entities.py create mode 100644 ark_sdk_python/models/services/identity/policies/__init__.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_add_authentication_profile.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_add_policy.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_authentication_profile.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_disable_policy.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_enable_policy.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_get_authentication_profile.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_get_policy.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_policy.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_policy_info.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation_type.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_remove_authentication_profile.py create mode 100644 ark_sdk_python/models/services/identity/policies/ark_identity_remove_policy.py create mode 100644 ark_sdk_python/models/services/identity/roles/__init__.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_add_admin_right_to_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_add_group_to_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_add_role_to_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_add_user_to_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_admin_right.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_create_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_delete_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_list_role_members.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_remove_group_from_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_remove_role_from_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_remove_user_from_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_role.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_role_id_by_name.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_role_member.py create mode 100644 ark_sdk_python/models/services/identity/roles/ark_identity_update_role.py create mode 100644 ark_sdk_python/models/services/identity/users/__init__.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_create_user.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_delete_user.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_reset_user_password.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_update_user.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_user.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_user_by_name.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_user_id_by_name.py create mode 100644 ark_sdk_python/models/services/identity/users/ark_identity_user_info.py create mode 100644 ark_sdk_python/services/identity/__init__.py create mode 100644 ark_sdk_python/services/identity/ark_identity_api.py create mode 100644 ark_sdk_python/services/identity/common/__init__.py create mode 100644 ark_sdk_python/services/identity/common/ark_identity_base_service.py create mode 100644 ark_sdk_python/services/identity/directories/__init__.py create mode 100644 ark_sdk_python/services/identity/directories/ark_identity_directories_service.py create mode 100644 ark_sdk_python/services/identity/policies/__init__.py create mode 100644 ark_sdk_python/services/identity/policies/ark_identity_policies_service.py create mode 100644 ark_sdk_python/services/identity/roles/__init__.py create mode 100644 ark_sdk_python/services/identity/roles/ark_identity_roles_service.py create mode 100644 ark_sdk_python/services/identity/users/__init__.py create mode 100644 ark_sdk_python/services/identity/users/ark_identity_users_service.py diff --git a/README.md b/README.md index fc712e63..6417bf7a 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,10 @@ CyberArk's Official SDK and CLI for different services operations - [x] DPA K8S Service - [x] DPA DB Service - [x] Session Monitoring Service + - [x] Identity Users Service + - [x] Identity Roles Service + - [x] Identity Policies Service + - [x] Identity Directories Service - [x] All services contains CRUD and Statistics per respective service - [x] Ready to use SDK in Python - [x] CLI and SDK Examples @@ -211,6 +215,12 @@ The following services and commands are supported: - db - DPA DB Enduser Operations - sso - DPA SSO Enduser Operations - k8s - DPA kubernetes service +- sm - Session Monitoring Service +- identity - Identity Service + - users - Identity Users Management + - roles - Identity Roles Management + - policies - Identity Policies Management + - directories - Identity Directories Reading Any command has its own subcommands, with respective arguments diff --git a/ark_sdk_python/ark_api.py b/ark_sdk_python/ark_api.py index de64e579..37fed323 100644 --- a/ark_sdk_python/ark_api.py +++ b/ark_sdk_python/ark_api.py @@ -80,6 +80,54 @@ def profile(self) -> ArkProfile: """ return self.__profile + @property + def identity_directories(self) -> "ArkIdentityDirectoriesService": + """ + Returns the Identity Directories Service if the appropriate authenticators were given + + Returns: + ArkIdentityDirectoriesService: _description_ + """ + from ark_sdk_python.services.identity.directories import ArkIdentityDirectoriesService + + return cast(ArkIdentityDirectoriesService, self.service(ArkIdentityDirectoriesService)) + + @property + def identity_policies(self) -> "ArkIdentityPoliciesService": + """ + Returns the Identity Policies Service if the appropriate authenticators were given + + Returns: + ArkIdentityPoliciesService: _description_ + """ + from ark_sdk_python.services.identity.policies import ArkIdentityPoliciesService + + return cast(ArkIdentityPoliciesService, self.service(ArkIdentityPoliciesService)) + + @property + def identity_roles(self) -> "ArkIdentityRolesService": + """ + Returns the Identity Roles Service if the appropriate authenticators were given + + Returns: + ArkIdentityRolesService: _description_ + """ + from ark_sdk_python.services.identity.roles import ArkIdentityRolesService + + return cast(ArkIdentityRolesService, self.service(ArkIdentityRolesService)) + + @property + def identity_users(self) -> "ArkIdentityUsersService": + """ + Returns the Identity Users Service if the appropriate authenticators were given + + Returns: + ArkIdentityUsersService: _description_ + """ + from ark_sdk_python.services.identity.users import ArkIdentityUsersService + + return cast(ArkIdentityUsersService, self.service(ArkIdentityUsersService)) + @property def dpa_workspaces_db(self) -> "ArkDPADBWorkspaceService": """ diff --git a/ark_sdk_python/examples/default_suffix.py b/ark_sdk_python/examples/default_suffix.py new file mode 100644 index 00000000..91920863 --- /dev/null +++ b/ark_sdk_python/examples/default_suffix.py @@ -0,0 +1,12 @@ +from ark_sdk_python.auth import ArkISPAuth +from ark_sdk_python.models.ark_profile import ArkProfileLoader +from ark_sdk_python.models.services.identity.directories import ArkIdentityListDirectoriesEntities +from ark_sdk_python.services.identity import ArkIdentityAPI + +if __name__ == "__main__": + isp_auth = ArkISPAuth() + isp_auth.authenticate(ArkProfileLoader().load_default_profile()) + identity_api = ArkIdentityAPI(isp_auth) + print(identity_api.identity_directories.tenant_default_suffix()) + for page in identity_api.identity_directories.list_directories_entities(ArkIdentityListDirectoriesEntities()): + print([i.name for i in page.items]) diff --git a/ark_sdk_python/models/actions/services/__init__.py b/ark_sdk_python/models/actions/services/__init__.py index 11f77a61..c7779252 100644 --- a/ark_sdk_python/models/actions/services/__init__.py +++ b/ark_sdk_python/models/actions/services/__init__.py @@ -1,14 +1,17 @@ from typing import Any, List from ark_sdk_python.models.actions.services.ark_dpa_exec_action_consts import DPA_ACTIONS +from ark_sdk_python.models.actions.services.ark_identity_exec_action_consts import IDENTITY_ACTIONS from ark_sdk_python.models.actions.services.ark_sm_exec_action_consts import SM_ACTIONS SUPPORTED_SERVICE_ACTIONS: List[Any] = [ + IDENTITY_ACTIONS, DPA_ACTIONS, SM_ACTIONS, ] __all__ = [ + 'IDENTITY_ACTIONS', 'DPA_ACTIONS', 'SM_ACTIONS', 'SUPPORTED_SERVICE_ACTIONS', diff --git a/ark_sdk_python/models/actions/services/ark_identity_exec_action_consts.py b/ark_sdk_python/models/actions/services/ark_identity_exec_action_consts.py new file mode 100644 index 00000000..3c1ccf8b --- /dev/null +++ b/ark_sdk_python/models/actions/services/ark_identity_exec_action_consts.py @@ -0,0 +1,114 @@ +from typing import Dict, Final, Optional, Type + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.actions.ark_service_action_definition import ArkServiceActionDefinition +from ark_sdk_python.models.services.identity.directories import ArkIdentityListDirectories, ArkIdentityListDirectoriesEntities +from ark_sdk_python.models.services.identity.policies import ( + ArkIdentityAddAuthenticationProfile, + ArkIdentityAddPolicy, + ArkIdentityDisablePolicy, + ArkIdentityEnablePolicy, + ArkIdentityGetAuthenticationProfile, + ArkIdentityGetPolicy, + ArkIdentityRemoveAuthenticationProfile, + ArkIdentityRemovePolicy, +) +from ark_sdk_python.models.services.identity.roles import ( + ArkIdentityAddAdminRightsToRole, + ArkIdentityAddGroupToRole, + ArkIdentityAddRoleToRole, + ArkIdentityAddUserToRole, + ArkIdentityCreateRole, + ArkIdentityDeleteRole, + ArkIdentityListRoleMembers, + ArkIdentityRemoveGroupFromRole, + ArkIdentityRemoveRoleFromRole, + ArkIdentityRemoveUserFromRole, + ArkIdentityRoleIdByName, + ArkIdentityUpdateRole, +) +from ark_sdk_python.models.services.identity.users import ( + ArkIdentityCreateUser, + ArkIdentityDeleteUser, + ArkIdentityResetUserPassword, + ArkIdentityUpdateUser, + ArkIdentityUserByName, + ArkIdentityUserIdByName, +) + +# Identity Definitions +# Directories +IDENTITY_DIRECTORIES_ACTION_TO_SCHEMA_MAP: Final[Dict[str, Optional[Type[ArkModel]]]] = { + 'list-directories': ArkIdentityListDirectories, + 'list-directories-entities': ArkIdentityListDirectoriesEntities, + 'tenant-default-suffix': None, +} +IDENTITY_DIRECTORIES_ACTIONS: Final[ArkServiceActionDefinition] = ArkServiceActionDefinition( + action_name='directories', + schemas=IDENTITY_DIRECTORIES_ACTION_TO_SCHEMA_MAP, +) + +# Policies +IDENTITY_POLICIES_ACTION_TO_SCHEMA_MAP: Final[Dict[str, Optional[Type[ArkModel]]]] = { + 'add-authentication-profile': ArkIdentityAddAuthenticationProfile, + 'remove-authentication-profile': ArkIdentityRemoveAuthenticationProfile, + 'list-authentication-profiles': None, + 'authentication-profile': ArkIdentityGetAuthenticationProfile, + 'add-policy': ArkIdentityAddPolicy, + 'remove-policy': ArkIdentityRemovePolicy, + 'list-policies': None, + 'policy': ArkIdentityGetPolicy, + 'enable-policy': ArkIdentityEnablePolicy, + 'disable-policy': ArkIdentityDisablePolicy, + 'enable-default-policy': None, + 'disable-default-policy': None, +} +IDENTITY_POLICIES_ACTIONS: Final[ArkServiceActionDefinition] = ArkServiceActionDefinition( + action_name='policies', + schemas=IDENTITY_POLICIES_ACTION_TO_SCHEMA_MAP, +) + +# Roles +IDENTITY_ROLES_ACTION_TO_SCHEMA_MAP: Final[Dict[str, Optional[Type[ArkModel]]]] = { + 'add-user-to-role': ArkIdentityAddUserToRole, + 'add-group-to-role': ArkIdentityAddGroupToRole, + 'add-role-to-role': ArkIdentityAddRoleToRole, + 'remove-user-from-role': ArkIdentityRemoveUserFromRole, + 'remove-group-from-role': ArkIdentityRemoveGroupFromRole, + 'remove-role-from-role': ArkIdentityRemoveRoleFromRole, + 'create-role': ArkIdentityCreateRole, + 'update-role': ArkIdentityUpdateRole, + 'delete-role': ArkIdentityDeleteRole, + 'list-role-members': ArkIdentityListRoleMembers, + 'add-admin-rights-to-role': ArkIdentityAddAdminRightsToRole, + 'role-id-by-name': ArkIdentityRoleIdByName, +} +IDENTITY_ROLES_ACTIONS: Final[ArkServiceActionDefinition] = ArkServiceActionDefinition( + action_name='roles', + schemas=IDENTITY_ROLES_ACTION_TO_SCHEMA_MAP, +) + +# Users +IDENTITY_USERS_ACTION_TO_SCHEMA_MAP: Final[Dict[str, Optional[Type[ArkModel]]]] = { + 'create-user': ArkIdentityCreateUser, + 'update-user': ArkIdentityUpdateUser, + 'delete-user': ArkIdentityDeleteUser, + 'user-by-name': ArkIdentityUserByName, + 'user-id-by-name': ArkIdentityUserIdByName, + 'reset-user-password': ArkIdentityResetUserPassword, +} +IDENTITY_USERS_ACTIONS: Final[ArkServiceActionDefinition] = ArkServiceActionDefinition( + action_name='users', + schemas=IDENTITY_USERS_ACTION_TO_SCHEMA_MAP, +) + +# Service Actions Definition +IDENTITY_ACTIONS: Final[ArkServiceActionDefinition] = ArkServiceActionDefinition( + action_name='identity', + subactions=[ + IDENTITY_DIRECTORIES_ACTIONS, + IDENTITY_POLICIES_ACTIONS, + IDENTITY_ROLES_ACTIONS, + IDENTITY_USERS_ACTIONS, + ], +) diff --git a/ark_sdk_python/models/services/identity/__init__.py b/ark_sdk_python/models/services/identity/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/ark_sdk_python/models/services/identity/directories/__init__.py b/ark_sdk_python/models/services/identity/directories/__init__.py new file mode 100644 index 00000000..34c2f5da --- /dev/null +++ b/ark_sdk_python/models/services/identity/directories/__init__.py @@ -0,0 +1,21 @@ +from ark_sdk_python.models.services.identity.directories.ark_identity_directory import ArkIdentityDirectory +from ark_sdk_python.models.services.identity.directories.ark_identity_entity import ( + ArkIdentityEntity, + ArkIdentityEntityType, + ArkIdentityGroupEntity, + ArkIdentityRoleEntity, + ArkIdentityUserEntity, +) +from ark_sdk_python.models.services.identity.directories.ark_identity_list_directories import ArkIdentityListDirectories +from ark_sdk_python.models.services.identity.directories.ark_identity_list_directories_entities import ArkIdentityListDirectoriesEntities + +__all__ = [ + 'ArkIdentityListDirectoriesEntities', + 'ArkIdentityEntity', + 'ArkIdentityEntityType', + 'ArkIdentityGroupEntity', + 'ArkIdentityRoleEntity', + 'ArkIdentityUserEntity', + 'ArkIdentityListDirectories', + 'ArkIdentityDirectory', +] diff --git a/ark_sdk_python/models/services/identity/directories/ark_identity_directory.py b/ark_sdk_python/models/services/identity/directories/ark_identity_directory.py new file mode 100644 index 00000000..75422a2f --- /dev/null +++ b/ark_sdk_python/models/services/identity/directories/ark_identity_directory.py @@ -0,0 +1,9 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.common.identity import DirectoryService + + +class ArkIdentityDirectory(ArkModel): + directory: DirectoryService = Field(description='Name of the directory') + directory_service_uuid: str = Field(description='ID of the directory') diff --git a/ark_sdk_python/models/services/identity/directories/ark_identity_entity.py b/ark_sdk_python/models/services/identity/directories/ark_identity_entity.py new file mode 100644 index 00000000..97211865 --- /dev/null +++ b/ark_sdk_python/models/services/identity/directories/ark_identity_entity.py @@ -0,0 +1,37 @@ +from enum import Enum +from typing import List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.common.identity import DirectoryService, RoleAdminRight + + +class ArkIdentityEntityType(str, Enum): + Role = 'ROLE' + User = 'USER' + Group = 'GROUP' + + +class ArkIdentityEntity(ArkModel): + id: str = Field(description='ID of the entity') + name: str = Field(description='Name of the entity') + entity_type: ArkIdentityEntityType = Field(description='Type of the entity') + directory_service_type: DirectoryService = Field(description='Directory type of the entity') + display_name: Optional[str] = Field(description='Display name of the entity') + service_instance_localized: str = Field(description='Display directory service name') + + +class ArkIdentityUserEntity(ArkIdentityEntity): + email: Optional[str] = Field(description='Email of the user') + description: Optional[str] = Field(description='Description of the user') + + +class ArkIdentityGroupEntity(ArkIdentityEntity): + pass + + +class ArkIdentityRoleEntity(ArkIdentityEntity): + admin_rights: Optional[List[RoleAdminRight]] = Field(description='Admin rights of the role') + is_hidden: bool = Field(description='Whwether this role is hidden or not') + description: Optional[str] = Field(description='Description of the role') diff --git a/ark_sdk_python/models/services/identity/directories/ark_identity_list_directories.py b/ark_sdk_python/models/services/identity/directories/ark_identity_list_directories.py new file mode 100644 index 00000000..01b59f71 --- /dev/null +++ b/ark_sdk_python/models/services/identity/directories/ark_identity_list_directories.py @@ -0,0 +1,10 @@ +from typing import List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.common.identity import DirectoryService + + +class ArkIdentityListDirectories(ArkModel): + directories: Optional[List[DirectoryService]] = Field(description='Directories types to list') diff --git a/ark_sdk_python/models/services/identity/directories/ark_identity_list_directories_entities.py b/ark_sdk_python/models/services/identity/directories/ark_identity_list_directories_entities.py new file mode 100644 index 00000000..9311e7b4 --- /dev/null +++ b/ark_sdk_python/models/services/identity/directories/ark_identity_list_directories_entities.py @@ -0,0 +1,22 @@ +from typing import Final, List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.common.identity import DirectoryService +from ark_sdk_python.models.services.identity.directories.ark_identity_entity import ArkIdentityEntityType + +DEFAULT_ENTITIES_PAGE_SIZE: Final[int] = 10000 +DEFAULT_ENTITIES_LIMIT: Final[int] = 10000 +DEFAULT_MAX_PAGE_SIZE: Final[int] = -1 + + +class ArkIdentityListDirectoriesEntities(ArkModel): + directories: Optional[List[DirectoryService]] = Field(description='Directories to search on') + entity_types: Optional[List[ArkIdentityEntityType]] = Field( + description='Member types to search in the format of X,Y,Z, possible values are ROLE,USER,GROUP' + ) + search: Optional[str] = Field(description='Search string to use') + page_size: int = Field(description='Page size to emit', default=DEFAULT_ENTITIES_PAGE_SIZE) + limit: int = Field(description='Limit amount to list', default=DEFAULT_ENTITIES_LIMIT) + max_page_count: int = Field(description='Max page count to reach to', default=DEFAULT_MAX_PAGE_SIZE) diff --git a/ark_sdk_python/models/services/identity/policies/__init__.py b/ark_sdk_python/models/services/identity/policies/__init__.py new file mode 100644 index 00000000..88c02fc9 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/__init__.py @@ -0,0 +1,31 @@ +from ark_sdk_python.models.services.identity.policies.ark_identity_add_authentication_profile import ArkIdentityAddAuthenticationProfile +from ark_sdk_python.models.services.identity.policies.ark_identity_add_policy import ArkIdentityAddPolicy +from ark_sdk_python.models.services.identity.policies.ark_identity_authentication_profile import ArkIdentityAuthenticationProfile +from ark_sdk_python.models.services.identity.policies.ark_identity_disable_policy import ArkIdentityDisablePolicy +from ark_sdk_python.models.services.identity.policies.ark_identity_enable_policy import ArkIdentityEnablePolicy +from ark_sdk_python.models.services.identity.policies.ark_identity_get_authentication_profile import ArkIdentityGetAuthenticationProfile +from ark_sdk_python.models.services.identity.policies.ark_identity_get_policy import ArkIdentityGetPolicy +from ark_sdk_python.models.services.identity.policies.ark_identity_policy import ArkIdentityPolicy +from ark_sdk_python.models.services.identity.policies.ark_identity_policy_info import ArkIdentityPolicyInfo +from ark_sdk_python.models.services.identity.policies.ark_identity_policy_operation import ArkIdentityPolicyOperation +from ark_sdk_python.models.services.identity.policies.ark_identity_policy_operation_type import ArkIdentityPolicyOperationType +from ark_sdk_python.models.services.identity.policies.ark_identity_remove_authentication_profile import ( + ArkIdentityRemoveAuthenticationProfile, +) +from ark_sdk_python.models.services.identity.policies.ark_identity_remove_policy import ArkIdentityRemovePolicy + +__all__ = [ + 'ArkIdentityPolicy', + 'ArkIdentityPolicyInfo', + 'ArkIdentityDisablePolicy', + 'ArkIdentityEnablePolicy', + 'ArkIdentityPolicyOperationType', + 'ArkIdentityPolicyOperation', + 'ArkIdentityAddPolicy', + 'ArkIdentityGetPolicy', + 'ArkIdentityRemovePolicy', + 'ArkIdentityAddAuthenticationProfile', + 'ArkIdentityAuthenticationProfile', + 'ArkIdentityGetAuthenticationProfile', + 'ArkIdentityRemoveAuthenticationProfile', +] diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_add_authentication_profile.py b/ark_sdk_python/models/services/identity/policies/ark_identity_add_authentication_profile.py new file mode 100644 index 00000000..d0b3ded9 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_add_authentication_profile.py @@ -0,0 +1,13 @@ +from typing import Any, Dict, List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityAddAuthenticationProfile(ArkModel): + auth_profile_name: str = Field(description='Name of the profile') + first_challenges: List[str] = Field(description='List of first challenges for the profile, i,e "UP,SMS"') + second_challenges: Optional[List[str]] = Field(description='List of second challenges for the profile, i,e "UP,SMS"') + additional_data: Optional[Dict[str, Any]] = Field(description='Additional auth profile data') + duration_in_minutes: int = Field(description='Duration of auth profile', default=30) diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_add_policy.py b/ark_sdk_python/models/services/identity/policies/ark_identity_add_policy.py new file mode 100644 index 00000000..a47082b4 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_add_policy.py @@ -0,0 +1,13 @@ +from typing import Any, Dict, List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityAddPolicy(ArkModel): + policy_name: str = Field(description='Name of the policy to create') + description: str = Field(description='Description of the policy', default="") + role_names: List[str] = Field(description='Roles to associate to the policy') + auth_profile_name: str = Field(description='Authentication profile to assoicate to the policy') + settings: Optional[Dict[str, Any]] = Field(description='Settings of the policy') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_authentication_profile.py b/ark_sdk_python/models/services/identity/policies/ark_identity_authentication_profile.py new file mode 100644 index 00000000..17aab9b7 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_authentication_profile.py @@ -0,0 +1,14 @@ +from typing import Any, Dict, List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkTitleizedModel + + +class ArkIdentityAuthenticationProfile(ArkTitleizedModel): + challenges: List[str] = Field(description='List of challenges for the profile') + id: Optional[str] = Field(description='Identifier of the profile', alias='ID') + name: str = Field(description='Name of the profile') + single_challenge_mechanisms: Optional[str] = Field(description='Single challenge mechanisms used') + uuid: str = Field(description='UUID of the profile') + additional_data: Dict[str, Any] = Field(description='Additional profile data') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_disable_policy.py b/ark_sdk_python/models/services/identity/policies/ark_identity_disable_policy.py new file mode 100644 index 00000000..18fc4d2e --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_disable_policy.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityDisablePolicy(ArkModel): + policy_name: str = Field(description='Policy to disable') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_enable_policy.py b/ark_sdk_python/models/services/identity/policies/ark_identity_enable_policy.py new file mode 100644 index 00000000..bc7263ce --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_enable_policy.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityEnablePolicy(ArkModel): + policy_name: str = Field(description='Policy to enable') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_get_authentication_profile.py b/ark_sdk_python/models/services/identity/policies/ark_identity_get_authentication_profile.py new file mode 100644 index 00000000..a1155d99 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_get_authentication_profile.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityGetAuthenticationProfile(ArkModel): + auth_profile_id: Optional[str] = Field(description='Gets the profile by id') + auth_profile_name: Optional[str] = Field(description='Gets the profile by name') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_get_policy.py b/ark_sdk_python/models/services/identity/policies/ark_identity_get_policy.py new file mode 100644 index 00000000..990b0683 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_get_policy.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityGetPolicy(ArkModel): + policy_name: str = Field(description='Policy name to get') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_policy.py b/ark_sdk_python/models/services/identity/policies/ark_identity_policy.py new file mode 100644 index 00000000..15c90d1e --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_policy.py @@ -0,0 +1,25 @@ +from typing import Any, Dict, List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkTitleizedModel +from ark_sdk_python.models.services.identity.policies.ark_identity_authentication_profile import ArkIdentityAuthenticationProfile + + +class ArkIdentityPolicyDirectoryService(ArkTitleizedModel): + display_name_short: str = Field(description='Display name of the directory service') + directory_service_uuid: str = Field(description='UUID of the directory', alias='directoryServiceUuid') + + +class ArkIdentityPolicy(ArkTitleizedModel): + auth_profiles: List[ArkIdentityAuthenticationProfile] = Field(description="Auth profiles set for the policy") + description: str = Field(description='Description of the policy') + directory_services: List[ArkIdentityPolicyDirectoryService] = Field(description='Directory services of the policy') + path: str = Field(description='Path of the policy') + policy_modifiers: List[str] = Field(description='Modifiers of the policy') + radius_client_list: Optional[List[str]] = Field(description='Client list integrated with radius on the policy') + radius_server_list: Optional[List[str]] = Field(description='Server list integrated with radius on the policy') + rev_stamp: str = Field(description='Timestamp of the revision') + risk_analysis_levels: Dict[str, Any] = Field(description='Risk analysis info') + settings: Dict[str, Any] = Field(description='Settings of the policy') + version: int = Field(description='Version of the policy') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_policy_info.py b/ark_sdk_python/models/services/identity/policies/ark_identity_policy_info.py new file mode 100644 index 00000000..a9fc2b97 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_policy_info.py @@ -0,0 +1,11 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkTitleizedModel + + +class ArkIdentityPolicyInfo(ArkTitleizedModel): + id: str = Field(description='ID of the policy', alias='ID') + description: str = Field(description='Description of the policy') + enable_compliant: bool = Field(description='Enable policy compliant') + link_type: str = Field(description='Type of policy link') + policy_set: str = Field(description="Policy set name") diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation.py b/ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation.py new file mode 100644 index 00000000..07af6998 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation.py @@ -0,0 +1,9 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.services.identity.policies.ark_identity_policy_operation_type import ArkIdentityPolicyOperationType + + +class ArkIdentityPolicyOperation(ArkModel): + policy_name: str = Field(description='Policy name') + operation_type: ArkIdentityPolicyOperationType = Field(description='Operation to perform on the policy') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation_type.py b/ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation_type.py new file mode 100644 index 00000000..7609451a --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_policy_operation_type.py @@ -0,0 +1,6 @@ +from enum import Enum + + +class ArkIdentityPolicyOperationType(str, Enum): + ENABLE = 'Global' + DISABLE = 'Inactive' diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_remove_authentication_profile.py b/ark_sdk_python/models/services/identity/policies/ark_identity_remove_authentication_profile.py new file mode 100644 index 00000000..7e5305d2 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_remove_authentication_profile.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRemoveAuthenticationProfile(ArkModel): + auth_profile_id: Optional[str] = Field(description='Remove the profile by id') + auth_profile_name: Optional[str] = Field(description='Remove the profile by name') diff --git a/ark_sdk_python/models/services/identity/policies/ark_identity_remove_policy.py b/ark_sdk_python/models/services/identity/policies/ark_identity_remove_policy.py new file mode 100644 index 00000000..967ea318 --- /dev/null +++ b/ark_sdk_python/models/services/identity/policies/ark_identity_remove_policy.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRemovePolicy(ArkModel): + policy_name: str = Field(description='Policy name to remove') diff --git a/ark_sdk_python/models/services/identity/roles/__init__.py b/ark_sdk_python/models/services/identity/roles/__init__.py new file mode 100644 index 00000000..86fbb52e --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/__init__.py @@ -0,0 +1,33 @@ +from ark_sdk_python.models.services.identity.roles.ark_identity_add_admin_right_to_role import ArkIdentityAddAdminRightsToRole +from ark_sdk_python.models.services.identity.roles.ark_identity_add_group_to_role import ArkIdentityAddGroupToRole +from ark_sdk_python.models.services.identity.roles.ark_identity_add_role_to_role import ArkIdentityAddRoleToRole +from ark_sdk_python.models.services.identity.roles.ark_identity_add_user_to_role import ArkIdentityAddUserToRole +from ark_sdk_python.models.services.identity.roles.ark_identity_admin_right import ArkIdentityAdminRights +from ark_sdk_python.models.services.identity.roles.ark_identity_create_role import ArkIdentityCreateRole +from ark_sdk_python.models.services.identity.roles.ark_identity_delete_role import ArkIdentityDeleteRole +from ark_sdk_python.models.services.identity.roles.ark_identity_list_role_members import ArkIdentityListRoleMembers +from ark_sdk_python.models.services.identity.roles.ark_identity_remove_group_from_role import ArkIdentityRemoveGroupFromRole +from ark_sdk_python.models.services.identity.roles.ark_identity_remove_role_from_role import ArkIdentityRemoveRoleFromRole +from ark_sdk_python.models.services.identity.roles.ark_identity_remove_user_from_role import ArkIdentityRemoveUserFromRole +from ark_sdk_python.models.services.identity.roles.ark_identity_role import ArkIdentityRole +from ark_sdk_python.models.services.identity.roles.ark_identity_role_id_by_name import ArkIdentityRoleIdByName +from ark_sdk_python.models.services.identity.roles.ark_identity_role_member import ArkIdentityRoleMember +from ark_sdk_python.models.services.identity.roles.ark_identity_update_role import ArkIdentityUpdateRole + +__all__ = [ + 'ArkIdentityAddAdminRightsToRole', + 'ArkIdentityAddUserToRole', + 'ArkIdentityAddGroupToRole', + 'ArkIdentityAddRoleToRole', + 'ArkIdentityAdminRights', + 'ArkIdentityCreateRole', + 'ArkIdentityDeleteRole', + 'ArkIdentityRemoveUserFromRole', + 'ArkIdentityRemoveGroupFromRole', + 'ArkIdentityRemoveRoleFromRole', + 'ArkIdentityRole', + 'ArkIdentityRoleIdByName', + 'ArkIdentityUpdateRole', + 'ArkIdentityListRoleMembers', + 'ArkIdentityRoleMember', +] diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_add_admin_right_to_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_add_admin_right_to_role.py new file mode 100644 index 00000000..3f5979c9 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_add_admin_right_to_role.py @@ -0,0 +1,12 @@ +from typing import Optional + +from pydantic import Field, conlist + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.services.identity.roles.ark_identity_admin_right import ArkIdentityAdminRights + + +class ArkIdentityAddAdminRightsToRole(ArkModel): + role_id: Optional[str] = Field(description='Role id to add admin rights to') + role_name: Optional[str] = Field(description='Role name to add admin rights to') + admin_rights: conlist(ArkIdentityAdminRights, min_items=1) = Field(description='Admin rights to add to the role') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_add_group_to_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_add_group_to_role.py new file mode 100644 index 00000000..14b41575 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_add_group_to_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityAddGroupToRole(ArkModel): + group_name: str = Field(description='Group name to add to the role') + role_name: str = Field(description='Name of the role to add the group to') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_add_role_to_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_add_role_to_role.py new file mode 100644 index 00000000..27453e84 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_add_role_to_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityAddRoleToRole(ArkModel): + role_name_to_add: str = Field(description='Role name to add to the role') + role_name: str = Field(description='Name of the role to add the role to') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_add_user_to_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_add_user_to_role.py new file mode 100644 index 00000000..573ca7a6 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_add_user_to_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityAddUserToRole(ArkModel): + username: str = Field(description='Username to add to the role') + role_name: str = Field(description='Name of the role to add the user to') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_admin_right.py b/ark_sdk_python/models/services/identity/roles/ark_identity_admin_right.py new file mode 100644 index 00000000..163a93ed --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_admin_right.py @@ -0,0 +1,10 @@ +from enum import Enum + + +class ArkIdentityAdminRights(str, Enum): + AdminPortalLogin = '/lib/rights/adminportallogin.json' + RoleManagement = '/lib/rights/roleman.json' + ReadOnlyRoleManagement = '/lib/rights/roroleman.json' + UserManagement = '/lib/rights/dsman.json' + Audit = 'ServiceRight/auditShowTile' + JIT = 'ServiceRight/dpaShowTile' diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_create_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_create_role.py new file mode 100644 index 00000000..36956f25 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_create_role.py @@ -0,0 +1,12 @@ +from typing import List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.services.identity.roles.ark_identity_admin_right import ArkIdentityAdminRights + + +class ArkIdentityCreateRole(ArkModel): + role_name: str = Field(description='Role name to create') + description: Optional[str] = Field(description='Description of the role') + admin_rights: List[ArkIdentityAdminRights] = Field(description='Admin rights to add to the role', default_factory=list) diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_delete_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_delete_role.py new file mode 100644 index 00000000..d300a267 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_delete_role.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityDeleteRole(ArkModel): + role_name: Optional[str] = Field(description='Role name to delete') + role_id: Optional[str] = Field(description='Role id to delete') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_list_role_members.py b/ark_sdk_python/models/services/identity/roles/ark_identity_list_role_members.py new file mode 100644 index 00000000..7f26eec5 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_list_role_members.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityListRoleMembers(ArkModel): + role_name: Optional[str] = Field(description='Name of the role to get members of') + role_id: Optional[str] = Field(description='ID of the role to get members of') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_remove_group_from_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_remove_group_from_role.py new file mode 100644 index 00000000..0833abeb --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_remove_group_from_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRemoveGroupFromRole(ArkModel): + group_name: str = Field(description='Group name to remove from the role') + role_name: str = Field(description='Name of the role to remove the group from') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_remove_role_from_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_remove_role_from_role.py new file mode 100644 index 00000000..b1f3584a --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_remove_role_from_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRemoveRoleFromRole(ArkModel): + role_name_to_remove: str = Field(description='Role name to remove from the role') + role_name: str = Field(description='Name of the role to remove the role from') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_remove_user_from_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_remove_user_from_role.py new file mode 100644 index 00000000..2ec8f5ac --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_remove_user_from_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRemoveUserFromRole(ArkModel): + username: str = Field(description='Username to remove from the role') + role_name: str = Field(description='Name of the role to remove the user from') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_role.py new file mode 100644 index 00000000..3a3507c7 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_role.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRole(ArkModel): + role_id: str = Field(description='Identifier of the role') + role_name: str = Field(description='Name of the role') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_role_id_by_name.py b/ark_sdk_python/models/services/identity/roles/ark_identity_role_id_by_name.py new file mode 100644 index 00000000..7958a8c9 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_role_id_by_name.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityRoleIdByName(ArkModel): + role_name: str = Field(description='Role name to find the id for') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_role_member.py b/ark_sdk_python/models/services/identity/roles/ark_identity_role_member.py new file mode 100644 index 00000000..9295ac36 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_role_member.py @@ -0,0 +1,10 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel +from ark_sdk_python.models.services.identity.directories.ark_identity_entity import ArkIdentityEntityType + + +class ArkIdentityRoleMember(ArkModel): + member_id: str = Field(description='ID of the mmeber') + member_name: str = Field(description='Name of the member') + member_type: ArkIdentityEntityType = Field(description='Type of the member') diff --git a/ark_sdk_python/models/services/identity/roles/ark_identity_update_role.py b/ark_sdk_python/models/services/identity/roles/ark_identity_update_role.py new file mode 100644 index 00000000..d1942c86 --- /dev/null +++ b/ark_sdk_python/models/services/identity/roles/ark_identity_update_role.py @@ -0,0 +1,12 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityUpdateRole(ArkModel): + role_name: Optional[str] = Field(description='Role name to update') + role_id: Optional[str] = Field(description='Role id to update') + new_role_name: Optional[str] = Field(description='New role name to update to') + description: Optional[str] = Field(description='New description of the role') diff --git a/ark_sdk_python/models/services/identity/users/__init__.py b/ark_sdk_python/models/services/identity/users/__init__.py new file mode 100644 index 00000000..c08afb04 --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/__init__.py @@ -0,0 +1,19 @@ +from ark_sdk_python.models.services.identity.users.ark_identity_create_user import ArkIdentityCreateUser +from ark_sdk_python.models.services.identity.users.ark_identity_delete_user import ArkIdentityDeleteUser +from ark_sdk_python.models.services.identity.users.ark_identity_reset_user_password import ArkIdentityResetUserPassword +from ark_sdk_python.models.services.identity.users.ark_identity_update_user import ArkIdentityUpdateUser +from ark_sdk_python.models.services.identity.users.ark_identity_user import ArkIdentityUser +from ark_sdk_python.models.services.identity.users.ark_identity_user_by_name import ArkIdentityUserByName +from ark_sdk_python.models.services.identity.users.ark_identity_user_id_by_name import ArkIdentityUserIdByName +from ark_sdk_python.models.services.identity.users.ark_identity_user_info import ArkIdentityUserInfo + +__all__ = [ + 'ArkIdentityCreateUser', + 'ArkIdentityUser', + 'ArkIdentityResetUserPassword', + 'ArkIdentityDeleteUser', + 'ArkIdentityUserIdByName', + 'ArkIdentityUserByName', + 'ArkIdentityUpdateUser', + 'ArkIdentityUserInfo', +] diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_create_user.py b/ark_sdk_python/models/services/identity/users/ark_identity_create_user.py new file mode 100644 index 00000000..0657bbce --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_create_user.py @@ -0,0 +1,29 @@ +import random +import string +from typing import Final, List, Optional + +from pydantic import Field, SecretStr + +from ark_sdk_python.common import ArkRandomUtils +from ark_sdk_python.models import ArkModel + +DEFAULT_ADMIN_ROLES: Final[List[str]] = ["DpaAdmin", 'global auditor', "System Administrator"] + + +class ArkIdentityCreateUser(ArkModel): + username: str = Field( + description='Name of the user to create', default_factory=lambda: f"ark_user_{ArkRandomUtils.random_string(n=10)}" + ) + display_name: Optional[str] = Field( + description='Display name of the user', + default_factory=lambda: f'{ArkRandomUtils.random_string(5).capitalize()} {ArkRandomUtils.random_string(7).capitalize()}', + ) + email: Optional[str] = Field( + description='Email of the user', default_factory=lambda: f'{ArkRandomUtils.random_string(6).lower()}@email.com' + ) + mobile_number: Optional[str] = Field( + description='Mobile number of the user', default_factory=lambda: f'+44-987-654-{"".join(random.choices(string.digits, k=4))}' + ) + suffix: Optional[str] = Field(description='Suffix to use for the username') + password: SecretStr = Field(description='Password of the user', default_factory=lambda: SecretStr(ArkRandomUtils.random_password(n=25))) + roles: List[str] = Field(description='Roles to add the user to', default_factory=DEFAULT_ADMIN_ROLES.copy) diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_delete_user.py b/ark_sdk_python/models/services/identity/users/ark_identity_delete_user.py new file mode 100644 index 00000000..42a0f75a --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_delete_user.py @@ -0,0 +1,10 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityDeleteUser(ArkModel): + user_id: Optional[str] = Field(description='User ID to delete') + username: Optional[str] = Field(description='Username to delete') diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_reset_user_password.py b/ark_sdk_python/models/services/identity/users/ark_identity_reset_user_password.py new file mode 100644 index 00000000..3d58f0af --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_reset_user_password.py @@ -0,0 +1,8 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityResetUserPassword(ArkModel): + username: str = Field(description='Username to reset the password for') + new_password: str = Field(description='New password to reset to') diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_update_user.py b/ark_sdk_python/models/services/identity/users/ark_identity_update_user.py new file mode 100644 index 00000000..c8d24220 --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_update_user.py @@ -0,0 +1,14 @@ +from typing import Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityUpdateUser(ArkModel): + user_id: Optional[str] = Field(description='Users id that we change the details for') + username: Optional[str] = Field(description='Username that we change the details for') + new_username: Optional[str] = Field(description='Name of the user to change') + display_name: Optional[str] = Field(description='Display name of the user to change') + email: Optional[str] = Field(description='Email of the user to change') + mobile_number: Optional[str] = Field(description='Mobile number of the user to change') diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_user.py b/ark_sdk_python/models/services/identity/users/ark_identity_user.py new file mode 100644 index 00000000..6cf89660 --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_user.py @@ -0,0 +1,16 @@ +from datetime import datetime +from typing import List, Optional + +from pydantic import Field + +from ark_sdk_python.models import ArkPresentableModel + + +class ArkIdentityUser(ArkPresentableModel): + user_id: str = Field(description='User identifier') + username: str = Field(description='Name of the user') + display_name: str = Field(description='Display name of the user') + email: Optional[str] = Field(description='Email of the user') + mobile_number: Optional[str] = Field(description='Mobile number of the user') + roles: Optional[List[str]] = Field(description='Roles of the user') + last_login: Optional[datetime] = Field(description='Last login of the user') diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_user_by_name.py b/ark_sdk_python/models/services/identity/users/ark_identity_user_by_name.py new file mode 100644 index 00000000..377033e6 --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_user_by_name.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityUserByName(ArkModel): + username: str = Field(description='User name to find the id for') diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_user_id_by_name.py b/ark_sdk_python/models/services/identity/users/ark_identity_user_id_by_name.py new file mode 100644 index 00000000..fe56d669 --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_user_id_by_name.py @@ -0,0 +1,7 @@ +from pydantic import Field + +from ark_sdk_python.models import ArkModel + + +class ArkIdentityUserIdByName(ArkModel): + username: str = Field(description='User name to find the id for') diff --git a/ark_sdk_python/models/services/identity/users/ark_identity_user_info.py b/ark_sdk_python/models/services/identity/users/ark_identity_user_info.py new file mode 100644 index 00000000..98808d66 --- /dev/null +++ b/ark_sdk_python/models/services/identity/users/ark_identity_user_info.py @@ -0,0 +1,16 @@ +from typing import Any, List + +from pydantic import Field + +from ark_sdk_python.models import ArkTitleizedModel + + +class ArkIdentityUserInfo(ArkTitleizedModel): + first_name: str = Field(description='First name of the user') + last_name: str = Field(description='Last name of the user') + home_number: str = Field(description='Home number of the user') + manager: str = Field(description='Manager of the user') + username: str = Field(description='Username info') + groups: List[Any] = Field(description='AD groups of the user', default_factory=list) + rights: List[str] = Field(description='Administrative rights of the user', default_factory=list) + roles: List[Any] = Field(description='Roles of the user', default_factory=list) diff --git a/ark_sdk_python/services/identity/__init__.py b/ark_sdk_python/services/identity/__init__.py new file mode 100644 index 00000000..1217600c --- /dev/null +++ b/ark_sdk_python/services/identity/__init__.py @@ -0,0 +1,3 @@ +from ark_sdk_python.services.identity.ark_identity_api import ArkIdentityAPI + +__all__ = ['ArkIdentityAPI'] diff --git a/ark_sdk_python/services/identity/ark_identity_api.py b/ark_sdk_python/services/identity/ark_identity_api.py new file mode 100644 index 00000000..4d6ca087 --- /dev/null +++ b/ark_sdk_python/services/identity/ark_identity_api.py @@ -0,0 +1,53 @@ +from ark_sdk_python.auth import ArkISPAuth +from ark_sdk_python.services.identity.directories import ArkIdentityDirectoriesService +from ark_sdk_python.services.identity.policies import ArkIdentityPoliciesService +from ark_sdk_python.services.identity.roles import ArkIdentityRolesService +from ark_sdk_python.services.identity.users import ArkIdentityUsersService + + +class ArkIdentityAPI: + def __init__(self, isp_auth: ArkISPAuth) -> None: + self.__identity_directories = ArkIdentityDirectoriesService(isp_auth) + self.__identity_policies = ArkIdentityPoliciesService(isp_auth) + self.__identity_roles = ArkIdentityRolesService(isp_auth) + self.__identity_users = ArkIdentityUsersService(isp_auth) + + @property + def identity_directories(self) -> ArkIdentityDirectoriesService: + """ + Getter for the identity directories service + + Returns: + ArkIdentityDirectoriesService: _description_ + """ + return self.__identity_directories + + @property + def identity_policies(self) -> ArkIdentityPoliciesService: + """ + Getter for the identity policies service + + Returns: + ArkIdentityPoliciesService: _description_ + """ + return self.__identity_policies + + @property + def identity_roles(self) -> ArkIdentityRolesService: + """ + Getter for the identity roles service + + Returns: + ArkIdentityRolesService: _description_ + """ + return self.__identity_roles + + @property + def identity_users(self) -> ArkIdentityUsersService: + """ + Getter for the identity users service + + Returns: + ArkIdentityUsersService: _description_ + """ + return self.__identity_users diff --git a/ark_sdk_python/services/identity/common/__init__.py b/ark_sdk_python/services/identity/common/__init__.py new file mode 100644 index 00000000..bb0d7f11 --- /dev/null +++ b/ark_sdk_python/services/identity/common/__init__.py @@ -0,0 +1,3 @@ +from ark_sdk_python.services.identity.common.ark_identity_base_service import ArkIdentityBaseService + +__all__ = ['ArkIdentityBaseService'] diff --git a/ark_sdk_python/services/identity/common/ark_identity_base_service.py b/ark_sdk_python/services/identity/common/ark_identity_base_service.py new file mode 100644 index 00000000..8fd0efb3 --- /dev/null +++ b/ark_sdk_python/services/identity/common/ark_identity_base_service.py @@ -0,0 +1,38 @@ +import os + +from ark_sdk_python.auth.ark_isp_auth import ArkISPAuth +from ark_sdk_python.common.ark_client import ArkClient +from ark_sdk_python.common.env import ROOT_DOMAIN, AwsEnv +from ark_sdk_python.common.isp import ArkISPServiceClient +from ark_sdk_python.services.ark_service import ArkService + + +class ArkIdentityBaseService(ArkService): + def __init__(self, isp_auth: ArkISPAuth) -> None: + super().__init__(isp_auth) + self._isp_auth = isp_auth + self._idp_client = ArkClient( + isp_auth.token.endpoint, + isp_auth.token.token.get_secret_value(), + ) + self._idp_client.add_headers( + { + 'Content-Type': 'application/json', + 'X-IDAP-NATIVE-CLIENT': 'true', + 'Authorization': f'Bearer {isp_auth.token.token.get_secret_value()}', + } + ) + self._client = None + self._url_prefix = 'api/idadmin/' + self._env = None + if 'env' in isp_auth.token.metadata.keys(): + self._env = AwsEnv(isp_auth.token.metadata['env']) + else: + self._env = AwsEnv(os.environ.get('DEPLOY_ENV', AwsEnv.PROD.value)) + try: + self._client: ArkISPServiceClient = ArkISPServiceClient.from_isp_auth(self._isp_auth) + self._env = self._client.tenant_env + except Exception: + self._client = self._idp_client + if any(f'id.{d}' in self._idp_client.base_url for d in ROOT_DOMAIN.values()): + self._url_prefix = '' diff --git a/ark_sdk_python/services/identity/directories/__init__.py b/ark_sdk_python/services/identity/directories/__init__.py new file mode 100644 index 00000000..6c86c237 --- /dev/null +++ b/ark_sdk_python/services/identity/directories/__init__.py @@ -0,0 +1,3 @@ +from ark_sdk_python.services.identity.directories.ark_identity_directories_service import ArkIdentityDirectoriesService + +__all__ = ['ArkIdentityDirectoriesService'] diff --git a/ark_sdk_python/services/identity/directories/ark_identity_directories_service.py b/ark_sdk_python/services/identity/directories/ark_identity_directories_service.py new file mode 100644 index 00000000..9f62ea74 --- /dev/null +++ b/ark_sdk_python/services/identity/directories/ark_identity_directories_service.py @@ -0,0 +1,216 @@ +from http import HTTPStatus +from typing import Final, Iterator, List + +from overrides import overrides +from pydantic.error_wrappers import ValidationError +from requests import Response +from requests.exceptions import JSONDecodeError + +from ark_sdk_python.common import ArkPage +from ark_sdk_python.common.env import SHELL_DOMAIN, check_if_identity_generated_suffix +from ark_sdk_python.models.ark_exceptions import ArkServiceException +from ark_sdk_python.models.common.identity import ( + DirectorySearchArgs, + DirectoryService, + DirectoryServiceQueryResponse, + GetDirectoryServicesResponse, + GetTenantSuffixResult, +) +from ark_sdk_python.models.common.identity.ark_identity_directory_schemas import DirectoryServiceQueryRequest +from ark_sdk_python.models.services import ArkServiceConfig +from ark_sdk_python.models.services.identity.directories import ( + ArkIdentityDirectory, + ArkIdentityEntity, + ArkIdentityEntityType, + ArkIdentityGroupEntity, + ArkIdentityListDirectories, + ArkIdentityListDirectoriesEntities, + ArkIdentityRoleEntity, + ArkIdentityUserEntity, +) +from ark_sdk_python.services.identity.common import ArkIdentityBaseService + +SERVICE_CONFIG: Final[ArkServiceConfig] = ArkServiceConfig( + service_name='identity-directories', required_authenticator_names=['isp'], optional_authenticator_names=[] +) + +TENANT_SUFFIX_URL: Final[str] = 'Core/GetCdsAliasesForTenant' +GET_DIRECTORY_SERVICES_URL: Final[str] = 'Core/GetDirectoryServices' +DIRECTORY_SERVICE_QUERY_URL: Final[str] = 'UserMgmt/DirectoryServiceQuery' + +ArkIdentityEntitiesPage = ArkPage[ArkIdentityEntity] + + +class ArkIdentityDirectoriesService(ArkIdentityBaseService): + def list_directories(self, list_directories: ArkIdentityListDirectories) -> List[ArkIdentityDirectory]: + """ + Get directories for given types + + Args: + list_directories (ArkIdentityListDirectories): _description_ + + Raises: + ArkServiceException: _description_ + + Returns: + List[ArkIdentityDirectory]: _description_ + """ + if not list_directories.directories: + list_directories.directories = [d for d in DirectoryService] + self._logger.info(f'Retrieving directory services for directories [{list_directories}] [{self._url_prefix}]') + response: Response = self._client.get(f'{self._url_prefix}{GET_DIRECTORY_SERVICES_URL}', data={}) + try: + directory_services_result = GetDirectoryServicesResponse.parse_raw(response.text) + requested_directories = set(item.value for item in list_directories.directories) + requested_services = list( + filter(lambda service: service.row.service in requested_directories, directory_services_result.result.results) + ) + if len(requested_services) == 0: + raise ArkServiceException(f'Could not find any directory services matching {requested_directories}') + directories = list( + map( + lambda service: ArkIdentityDirectory( + directory=DirectoryService(service.row.service), directory_service_uuid=service.row.directory_service_uuid + ), + requested_services, + ) + ) + return directories + except (ValidationError, JSONDecodeError) as ex: + self._logger.exception(f'Failed to parse directory services response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse directory services response [{str(ex)}]') from ex + + def list_directories_entities(self, list_directories_entities: ArkIdentityListDirectoriesEntities) -> Iterator[ArkIdentityEntitiesPage]: + """ + Lists given directories entities by filters of search and type and directories + Yields pages of entities + + Args: + list_directories_entities (ArkIdentityListDirectoriesEntities): _description_ + + Raises: + ArkServiceException: _description_ + + Yields: + Iterator[ArkIdentityEntitiesPage]: _description_ + """ + self._logger.info('Listing directories entities') + directories = [ + d.directory_service_uuid + for d in self.list_directories( + ArkIdentityListDirectories(directories=list_directories_entities.directories or [d for d in DirectoryService]) + ) + ] + exclusion_list = set() + if list_directories_entities.entity_types: + if ArkIdentityEntityType.User not in list_directories_entities.entity_types: + exclusion_list.add('user') + if ArkIdentityEntityType.Group not in list_directories_entities.entity_types: + exclusion_list.add('group') + if ArkIdentityEntityType.Role not in list_directories_entities.entity_types: + exclusion_list.add('roles') + response: Response = self._idp_client.post( + DIRECTORY_SERVICE_QUERY_URL, + data=DirectoryServiceQueryRequest( + directory_services=directories, + search_string=list_directories_entities.search, + args=DirectorySearchArgs( + limit=list_directories_entities.limit, page_number=1, page_size=list_directories_entities.page_size + ), + ).json(by_alias=True, exclude=exclusion_list), + ) + if response.status_code != HTTPStatus.OK: + raise ArkServiceException(f'Failed to query for directory services entities [{response.text}] - [{response.status_code}]') + try: + result = DirectoryServiceQueryResponse.parse_raw(response.text) + entities: List[ArkIdentityEntity] = [] + if result.result.users and result.result.users.results: + for user in result.result.users.results: + entities.append( + ArkIdentityUserEntity( + id=user.row.internal_id, + name=user.row.system_name, + entity_type=ArkIdentityEntityType.User, + directory_service_type=user.row.directory_service_type, + display_name=user.row.display_name, + service_instance_localized=user.row.service_instance_localized, + email=user.row.email, + description=user.row.description, + ) + ) + if result.result.groups and result.result.groups.results: + for group in result.result.groups.results: + entities.append( + ArkIdentityGroupEntity( + id=group.row.internal_id, + name=group.row.system_name, + entity_type=ArkIdentityEntityType.Group, + directory_service_type=group.row.directory_service_type, + display_name=group.row.display_name, + service_instance_localized=group.row.service_instance_localized, + ) + ) + if result.result.roles and result.result.roles.results: + for role in result.result.roles.results: + entities.append( + ArkIdentityRoleEntity( + id=role.row.id, + name=role.row.name, + entity_type=ArkIdentityEntityType.Role, + directory_service_type=DirectoryService.Identity, + display_name=role.row.name, + service_instance_localized=DirectoryService.Identity.value, + admin_rights=role.row.admin_rights, + is_hidden=role.row.is_hidden or False, + description=role.row.description, + ) + ) + while entities: + if len(entities) <= list_directories_entities.page_size: + yield ArkIdentityEntitiesPage(entities) + break + else: + page = entities[: list_directories_entities.page_size] + entities = entities[list_directories_entities.page_size :] + yield ArkIdentityEntitiesPage(page) + except (ValidationError, JSONDecodeError) as ex: + self._logger.exception(f'Failed to parse list directories entities response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse list directories entities response [{str(ex)}]') from ex + + def tenant_default_suffix(self) -> str: + """ + Retrieves the tenant default suffix found in identity + The suffix is used when creating users based on whats configured on the tenant + + Raises: + ArkServiceException: _description_ + + Returns: + str: _description_ + """ + self._logger.info('Discovering default tenant suffix') + response: Response = self._client.post(f'{self._url_prefix}{TENANT_SUFFIX_URL}') + if response.status_code != HTTPStatus.OK: + raise ArkServiceException(f'Failed to get directory services [{response.text}]') + try: + tenant_suffixes_result: GetTenantSuffixResult = GetTenantSuffixResult.parse_raw(response.text) + tenant_suffixes_list: List[str] = [result['Entities'][0]['Key'] for result in tenant_suffixes_result.result['Results']] + if len(tenant_suffixes_list) == 0: + raise ArkServiceException('No tenant suffix has been found') + filtered_urls = list( + filter( + lambda suffix: check_if_identity_generated_suffix(suffix, self._env) or SHELL_DOMAIN[self._env] in suffix, + tenant_suffixes_list, + ) + ) + if filtered_urls: + return filtered_urls[0] + return tenant_suffixes_list[0] + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse tenant default suffix response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse tenant default suffix response [{str(ex)}]') from ex + + @staticmethod + @overrides + def service_config() -> ArkServiceConfig: + return SERVICE_CONFIG diff --git a/ark_sdk_python/services/identity/policies/__init__.py b/ark_sdk_python/services/identity/policies/__init__.py new file mode 100644 index 00000000..bce09032 --- /dev/null +++ b/ark_sdk_python/services/identity/policies/__init__.py @@ -0,0 +1,3 @@ +from ark_sdk_python.services.identity.policies.ark_identity_policies_service import ArkIdentityPoliciesService + +__all__ = ['ArkIdentityPoliciesService'] diff --git a/ark_sdk_python/services/identity/policies/ark_identity_policies_service.py b/ark_sdk_python/services/identity/policies/ark_identity_policies_service.py new file mode 100644 index 00000000..e031e0e9 --- /dev/null +++ b/ark_sdk_python/services/identity/policies/ark_identity_policies_service.py @@ -0,0 +1,366 @@ +from http import HTTPStatus +from typing import Final, List + +from overrides import overrides +from pydantic import parse_obj_as +from pydantic.error_wrappers import ValidationError +from requests import Response +from requests.exceptions import JSONDecodeError + +from ark_sdk_python.models.ark_exceptions import ArkServiceException +from ark_sdk_python.models.services import ArkServiceConfig +from ark_sdk_python.models.services.identity.policies import ( + ArkIdentityAddAuthenticationProfile, + ArkIdentityAddPolicy, + ArkIdentityAuthenticationProfile, + ArkIdentityDisablePolicy, + ArkIdentityEnablePolicy, + ArkIdentityGetAuthenticationProfile, + ArkIdentityGetPolicy, + ArkIdentityPolicy, + ArkIdentityPolicyInfo, + ArkIdentityPolicyOperation, + ArkIdentityPolicyOperationType, + ArkIdentityRemoveAuthenticationProfile, + ArkIdentityRemovePolicy, +) +from ark_sdk_python.models.services.identity.roles import ArkIdentityRoleIdByName +from ark_sdk_python.services.identity.common import ArkIdentityBaseService +from ark_sdk_python.services.identity.roles import ArkIdentityRolesService + +SERVICE_CONFIG: Final[ArkServiceConfig] = ArkServiceConfig( + service_name='identity-policies', required_authenticator_names=['isp'], optional_authenticator_names=[] +) + +SAVE_PROFILE_URL: Final[str] = 'AuthProfile/SaveProfile' +DELETE_PROFILE_URL: Final[str] = 'AuthProfile/DeleteProfile' +GET_PROFILES_URL: Final[str] = 'AuthProfile/GetDecoratedProfileList' +SAVE_POLICY_URL: Final[str] = 'Policy/SavePolicyBlock3' +DELETE_POLICY_URL: Final[str] = 'Policy/DeletePolicyBlock' +LIST_POLICIES_URL: Final[str] = 'Policy/GetNicePlinks' +GET_POLICY_URL: Final[str] = 'Policy/GetPolicyBlock' + + +class ArkIdentityPoliciesService(ArkIdentityBaseService): + def add_authentication_profile( + self, add_authentication_profile: ArkIdentityAddAuthenticationProfile + ) -> ArkIdentityAuthenticationProfile: + """ + Adds a new authentication profile + + Args: + add_authentication_profile (ArkIdentityAddAuthenticationProfile): _description_ + + Returns: + ArkIdentityAuthenticationProfile: _description_ + """ + self._logger.info(f'Adding authentication profile [{add_authentication_profile.auth_profile_name}]') + data = { + 'settings': { + 'Name': add_authentication_profile.auth_profile_name, + 'Challenges': [','.join(add_authentication_profile.first_challenges)], + 'DurationInMinutes': add_authentication_profile.duration_in_minutes, + } + } + if add_authentication_profile.second_challenges: + data['settings']['Challenges'].append(','.join(add_authentication_profile.second_challenges)) + if add_authentication_profile.additional_data: + data['settings']['AdditionalData'] = add_authentication_profile.additional_data + response: Response = self._client.post(f'{self._url_prefix}{SAVE_PROFILE_URL}', json=data) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to add authentication profile [{response.text}] - [{response.status_code}]') + return ArkIdentityAuthenticationProfile.parse_obj(result['Result']) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse add authentication profile response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse add authentication profile response [{str(ex)}]') from ex + + def remove_authentication_profile(self, remove_authentication_profile: ArkIdentityRemoveAuthenticationProfile) -> None: + """ + Removes an authentication profile by name or id + + Args: + remove_authentication_profile (ArkIdentityRemoveAuthenticationProfile): _description_ + """ + if remove_authentication_profile.auth_profile_name and not remove_authentication_profile.auth_profile_id: + remove_authentication_profile.auth_profile_id = self.authentication_profile( + ArkIdentityGetAuthenticationProfile(auth_profile_name=remove_authentication_profile.auth_profile_name) + ).uuid + self._logger.info(f'Removing authentication profile [{remove_authentication_profile.auth_profile_id}]') + response: Response = self._client.post( + f'{self._url_prefix}{DELETE_PROFILE_URL}', json={'uuid': remove_authentication_profile.auth_profile_id} + ) + try: + if response.status_code != HTTPStatus.OK or not response.json()['success']: + raise ArkServiceException(f'Failed to remove authentication profile [{response.text}] - [{response.status_code}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse remove authentication profile response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse remove authentication profile response [{str(ex)}]') from ex + + def list_authentication_profiles(self) -> List[ArkIdentityAuthenticationProfile]: + """ + List available authentication profiles + + Raises: + ArkServiceException: _description_ + + Returns: + List[ArkIdentityAuthenticationProfile]: _description_ + """ + self._logger.info('Listing authentication profiles') + response: Response = self._client.post(f'{self._url_prefix}{GET_PROFILES_URL}') + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to list authentication profiles [{response.text}] - [{response.status_code}]') + return parse_obj_as(List[ArkIdentityAuthenticationProfile], [r['Row'] for r in result['Result']['Results']]) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse list authentication profiles response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse list authentication profiles response [{str(ex)}]') from ex + + def authentication_profile(self, get_authentication_profile: ArkIdentityGetAuthenticationProfile) -> ArkIdentityAuthenticationProfile: + """ + Retrieve an authentication profile by id or name + + Args: + get_authentication_profile (ArkIdentityGetAuthenticationProfile): _description_ + + Raises: + ArkServiceException: _description_ + + Returns: + ArkIdentityAuthenticationProfile: _description_ + """ + self._logger.info('Retrieving authentication profile') + auth_profiles = self.list_authentication_profiles() + if get_authentication_profile.auth_profile_id: + auth_profiles = [p for p in auth_profiles if p.uuid == get_authentication_profile.auth_profile_id] + if get_authentication_profile.auth_profile_name: + auth_profiles = [p for p in auth_profiles if p.name == get_authentication_profile.auth_profile_name] + if len(auth_profiles) == 0: + raise ArkServiceException('Failed to find authentication profile') + return auth_profiles[0] + + def add_policy(self, add_policy: ArkIdentityAddPolicy) -> ArkIdentityPolicy: + """ + Adds a new policy + + Args: + add_policy (ArkIdentityAddPolicy): _description_ + + Returns: + ArkIdentityPolicy: _description_ + """ + self._logger.info(f'Adding policy [{add_policy.policy_name}]') + roles_service = ArkIdentityRolesService(self._isp_auth) + policies_list = [p.dict(by_alias=True) for p in self.list_policies()] + policy_name = f'/Policy/{add_policy.policy_name}' + policy_link = { + "Description": add_policy.description, + "PolicySet": policy_name, + "LinkType": "Role", + "Priority": 1, + "Params": [roles_service.role_id_by_name(ArkIdentityRoleIdByName(role_name=role_name)) for role_name in add_policy.role_names], + "Filters": [], + "Allowedpolicies": [], + } + policies_list.insert(0, policy_link) + data = { + "plinks": policies_list, + "policy": { + "Path": policy_name, + "Version": 1, + "Description": add_policy.description, + "Settings": { + "AuthenticationEnabled": 'true', + "/Core/Authentication/AuthenticationRulesDefaultProfileId": self.authentication_profile( + ArkIdentityGetAuthenticationProfile(auth_profile_name=add_policy.auth_profile_name) + ).uuid, + "/Core/Authentication/CookieAllowPersist": 'false', + "/Core/Authentication/AuthSessionMaxConcurrent": 0, + "/Core/Authentication/AllowIwa": 'true', + "/Core/Authentication/IwaSetKnownEndpoint": 'false', + "/Core/Authentication/IwaSatisfiesAllMechs": 'false', + "/Core/Authentication/AllowZso": 'true', + "/Core/Authentication/ZsoSkipChallenge": 'true', + "/Core/Authentication/ZsoSetKnownEndpoint": 'false', + "/Core/Authentication/ZsoSatisfiesAllMechs": 'false', + "/Core/Authentication/NoMfaMechLogin": 'false', + "/Core/Authentication/FederatedLoginAllowsMfa": 'false', + "/Core/Authentication/FederatedLoginSatisfiesAllMechs": 'false', + "/Core/MfaRestrictions/BlockMobileMechsOnMobileLogin": 'false', + "/Core/Authentication/ContinueFailedSessions": 'true', + "/Core/Authentication/SkipMechsInFalseAdvance": 'true', + "/Core/Authentication/AllowLoginMfaCache": 'false', + }, + "Newpolicy": 'true', + }, + } + if add_policy.settings: + data['policy']['Settings'].update(add_policy.settings) + response: Response = self._client.post(f'{self._url_prefix}{SAVE_POLICY_URL}', json=data) + try: + if response.status_code != HTTPStatus.OK or not response.json()['success']: + raise ArkServiceException(f'Failed to add policy [{response.text}] - [{response.status_code}]') + return self.policy(ArkIdentityGetPolicy(policy_name=add_policy.policy_name)) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse add policy response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse add policy response [{str(ex)}]') from ex + + def disable_default_policy(self) -> None: + """ + Disables the default policy (makes it inactive) + """ + self.disable_policy( + disable_policy=ArkIdentityDisablePolicy( + policy_name='/Policy/Default Policy', + ), + ) + + def enable_default_policy(self) -> None: + """ + Enables the default policy (makes it active) + """ + self.enable_policy( + enable_policy=ArkIdentityEnablePolicy( + policy_name='/Policy/Default Policy', + ), + ) + + def perform_action_on_policy(self, policy_operation: ArkIdentityPolicyOperation) -> None: + """ + Performs operation on policy (enable/disable) + + Args: + policy_operation (ArkIdentityPolicyOperation): _description_ + + Raises: + ArkServiceException: _description_ + """ + policy_name = policy_operation.policy_name + policy = self.policy(get_policy=ArkIdentityGetPolicy(policy_name=policy_name)) + + policies_list = [p.dict(by_alias=True) for p in self.list_policies()] + for elem in policies_list: + if elem['ID'] == policy_name: + elem['LinkType'] = policy_operation.operation_type.value + data = { + "plinks": policies_list, + "policy": { + "Path": policy_name, + "Version": 1, + "Description": policy.description, + "RevStamp": policy.rev_stamp, + "Settings": policy.settings, + "Newpolicy": False, + }, + } + response: Response = self._client.post(f'{self._url_prefix}{SAVE_POLICY_URL}', json=data) + try: + if response.status_code != HTTPStatus.OK or not response.json()['success']: + raise ArkServiceException(f'Failed to add policy [{response.text}] - [{response.status_code}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse perform policy action response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse perform policy action response [{str(ex)}]') from ex + + def enable_policy(self, enable_policy: ArkIdentityEnablePolicy) -> None: + """ + Enables a policy by name + + Args: + enable_policy (ArkIdentityEnablePolicy): _description_ + + """ + self._logger.info(f'Making Policy [{enable_policy.policy_name}] active') + self.perform_action_on_policy( + policy_operation=ArkIdentityPolicyOperation( + policy_name=enable_policy.policy_name, + operation_type=ArkIdentityPolicyOperationType.ENABLE, + ), + ) + + def disable_policy(self, disable_policy: ArkIdentityDisablePolicy) -> None: + """ + Disables a policy by name + + Args: + disable_policy (ArkIdentityDisablePolicy): _description_ + + """ + self._logger.info(f'Making Policy [{disable_policy.policy_name}] inactive') + self.perform_action_on_policy( + policy_operation=ArkIdentityPolicyOperation( + policy_name=disable_policy.policy_name, + operation_type=ArkIdentityPolicyOperationType.DISABLE, + ), + ) + + def remove_policy(self, remove_policy: ArkIdentityRemovePolicy) -> None: + """ + Removes a policy by name + + Args: + remove_policy (ArkIdentityRemovePolicy): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Removing policy [{remove_policy.policy_name}]') + policy_name = remove_policy.policy_name + if not policy_name.startswith('/Policy/'): + policy_name = f'/Policy/{policy_name}' + response: Response = self._client.post(f'{self._url_prefix}{DELETE_POLICY_URL}', json={'path': policy_name}) + try: + if response.status_code != HTTPStatus.OK or not response.json()['success']: + raise ArkServiceException(f'Failed to remove policy [{response.text}] - [{response.status_code}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse remove policy response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse remove policy response [{str(ex)}]') from ex + + def list_policies(self) -> List[ArkIdentityPolicyInfo]: + """ + Lists all policies short info + + Returns: + List[ArkIdentityPolicyInfo]: _description_ + """ + self._logger.info('Listing all policies') + response: Response = self._client.post(f'{self._url_prefix}{LIST_POLICIES_URL}') + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to list policies [{response.text}] - [{response.status_code}]') + return parse_obj_as(List[ArkIdentityPolicyInfo], [p['Row'] for p in result['Result']['Results']]) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse list policies response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse list policies response [{str(ex)}]') from ex + + def policy(self, get_policy: ArkIdentityGetPolicy) -> ArkIdentityPolicy: + """ + Retrieves a policy full info by name + + Args: + get_policy (ArkIdentityGetPolicy): _description_ + + Returns: + ArkIdentityPolicy: _description_ + """ + self._logger.info(f'Retrieving policy [{get_policy.policy_name}]') + policy_name = get_policy.policy_name + if not policy_name.startswith('/Policy/'): + policy_name = f'/Policy/{policy_name}' + response: Response = self._client.post(f'{self._url_prefix}{GET_POLICY_URL}', json={'name': policy_name}) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to list policies [{response.text}] - [{response.status_code}]') + return ArkIdentityPolicy.parse_obj(result['Result']) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse policy response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse policy response [{str(ex)}]') from ex + + @staticmethod + @overrides + def service_config() -> ArkServiceConfig: + return SERVICE_CONFIG diff --git a/ark_sdk_python/services/identity/roles/__init__.py b/ark_sdk_python/services/identity/roles/__init__.py new file mode 100644 index 00000000..1f65f1e4 --- /dev/null +++ b/ark_sdk_python/services/identity/roles/__init__.py @@ -0,0 +1,3 @@ +from ark_sdk_python.services.identity.roles.ark_identity_roles_service import ArkIdentityRolesService + +__all__ = ['ArkIdentityRolesService'] diff --git a/ark_sdk_python/services/identity/roles/ark_identity_roles_service.py b/ark_sdk_python/services/identity/roles/ark_identity_roles_service.py new file mode 100644 index 00000000..7d93a006 --- /dev/null +++ b/ark_sdk_python/services/identity/roles/ark_identity_roles_service.py @@ -0,0 +1,402 @@ +from http import HTTPStatus +from typing import Final, List + +from overrides import overrides +from pydantic.error_wrappers import ValidationError +from requests import Response +from requests.exceptions import JSONDecodeError + +from ark_sdk_python.models.ark_exceptions import ArkServiceException +from ark_sdk_python.models.common.identity import ( + DirectorySearchArgs, + DirectoryService, + DirectoryServiceQueryResponse, + DirectoryServiceQuerySpecificRoleRequest, +) +from ark_sdk_python.models.services import ArkServiceConfig +from ark_sdk_python.models.services.identity.directories import ArkIdentityEntityType, ArkIdentityListDirectories +from ark_sdk_python.models.services.identity.roles import ( + ArkIdentityAddAdminRightsToRole, + ArkIdentityAddGroupToRole, + ArkIdentityAddRoleToRole, + ArkIdentityAddUserToRole, + ArkIdentityCreateRole, + ArkIdentityDeleteRole, + ArkIdentityListRoleMembers, + ArkIdentityRemoveGroupFromRole, + ArkIdentityRemoveRoleFromRole, + ArkIdentityRemoveUserFromRole, + ArkIdentityRole, + ArkIdentityRoleIdByName, + ArkIdentityRoleMember, + ArkIdentityUpdateRole, +) +from ark_sdk_python.services.identity.common import ArkIdentityBaseService +from ark_sdk_python.services.identity.directories import ArkIdentityDirectoriesService + +SERVICE_CONFIG: Final[ArkServiceConfig] = ArkServiceConfig( + service_name='identity-roles', required_authenticator_names=['isp'], optional_authenticator_names=[] +) + +ADD_USER_TO_ROLE_URL: Final[str] = 'SaasManage/AddUsersAndGroupsToRole' +CREATE_ROLE_URL: Final[str] = 'Roles/StoreRole' +UPDATE_ROLE_URL: Final[str] = 'Roles/UpdateRole' +ROLE_MEMBERS_URL: Final[str] = 'Roles/GetRoleMembers' +ADD_ADMIN_RIGHTS_TO_ROLE_URL: Final[str] = 'SaasManage/AssignSuperRights' +REMOVE_USER_FROM_ROLE_URL: Final[str] = 'SaasManage/RemoveUsersAndGroupsFromRole' +DELETE_ROLE_URL: Final[str] = 'SaasManage/DeleteRole' +DIRECTORY_SERVICE_QUERY_URL: Final[str] = 'UserMgmt/DirectoryServiceQuery' +REDROCK_QUERY: Final[str] = 'Redrock/query' + + +class ArkIdentityRolesService(ArkIdentityBaseService): + def create_role(self, create_role: ArkIdentityCreateRole) -> ArkIdentityRole: + """ + Creates a role by given name and adds admin rights to it + If the role exists, will only alter admin rights and return it + + Args: + create_role (ArkIdentityCreateRole): _description_ + + Raises: + ArkServiceException: _description_ + + Returns: + ArkIdentityRole: _description_ + """ + role_details = None + self._logger.info(f'Trying to create role [{create_role.role_name}]') + try: + # Role exists + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=create_role.role_name)) + role_details = ArkIdentityRole(role_name=create_role.role_name, role_id=role_id) + self._logger.info(f'Role already exists with id [{role_id}]') + except (ValidationError, Exception) as ex: + # Create the role + create_dict = {'Name': create_role.role_name} + if create_role.description: + create_dict['Description'] = create_role.description + response: Response = self._client.post(f'{self._url_prefix}{CREATE_ROLE_URL}', json=create_dict) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to create role [{response.text}]') from ex + role_id = result['Result']['_RowKey'] + role_details = ArkIdentityRole(role_name=create_role.role_name, role_id=role_id) + self._logger.info(f'Role created with id [{role_id}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse create role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse create role response [{str(ex)}]') from ex + # Add admin rights + if create_role.admin_rights: + self.add_admin_rights_to_role( + ArkIdentityAddAdminRightsToRole(role_id=role_details.role_id, admin_rights=create_role.admin_rights) + ) + return role_details + + def update_role(self, update_role: ArkIdentityUpdateRole) -> None: + """ + Updates a role details + + Args: + update_role (ArkIdentityUpdateRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + if update_role.role_name and not update_role.role_id: + update_role.role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=update_role.role_name)) + self._logger.info(f'Updating identity role [{update_role.role_id}]') + update_dict = {'Name': update_role.role_id} + if update_role.new_role_name: + update_dict['NewName'] = update_role.new_role_name + if update_role.description: + update_role['Description'] = update_role.description + response: Response = self._client.post(f'{self._url_prefix}{UPDATE_ROLE_URL}', json=update_dict) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to update role [{response.text}]') + self._logger.info('Role updated successfully') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse update role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse update role response [{str(ex)}]') from ex + + def list_role_members(self, list_role_members: ArkIdentityListRoleMembers) -> List[ArkIdentityRoleMember]: + """ + Lists a role members + + Args: + list_role_members (ArkIdentityListRoleMembers): _description_ + + Raises: + ArkServiceException: _description_ + + Returns: + List[ArkIdentityRoleMember]: _description_ + """ + if list_role_members.role_name and not list_role_members.role_id: + list_role_members.role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=list_role_members.role_name)) + self._logger.info(f'Listing identity role [{list_role_members.role_id}] members') + response: Response = self._client.post(f'{self._url_prefix}{ROLE_MEMBERS_URL}', json={'Name': list_role_members.role_id}) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to list role members [{response.text}]') + members = [] + if 'Result' in result and 'Results' in result['Result'] and len(result['Result']['Results']) > 0: + members = [ + ArkIdentityRoleMember( + member_id=r['Row']['Guid'], + member_name=r['Row']['Name'], + member_type=ArkIdentityEntityType(r['Row']['Type'].upper()), + ) + for r in result['Result']['Results'] + ] + self._logger.info('Listed role members successfully successfully') + return members + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse list role members response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse list role members response [{str(ex)}]') from ex + + def add_admin_rights_to_role(self, add_admin_rights_to_role: ArkIdentityAddAdminRightsToRole) -> None: + """ + Adds given admin rights to the role assuming it exists + + Args: + add_admin_rights_to_role (ArkIdentityAddAdminRightsToRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Adding admin rights [{add_admin_rights_to_role.admin_rights}] to role [{add_admin_rights_to_role.role_name}]') + if not add_admin_rights_to_role.role_id and not add_admin_rights_to_role.role_name: + raise ArkServiceException('Either role id or role name must be given') + if add_admin_rights_to_role.role_id: + role_id = add_admin_rights_to_role.role_id + else: + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=add_admin_rights_to_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{ADD_ADMIN_RIGHTS_TO_ROLE_URL}', + json=[{'Role': role_id, 'Path': admin_right.value} for admin_right in add_admin_rights_to_role.admin_rights], + ) + try: + if response.status_code != HTTPStatus.OK or not response.json()['success']: + raise ArkServiceException(f'Failed to add admin rights to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse add admin rights to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse add admin rights to role response [{str(ex)}]') from ex + + def role_id_by_name(self, role_id_by_name: ArkIdentityRoleIdByName) -> str: + """ + For a given role name, find its identifier on identity + + Args: + role_id_by_name (ArkIdentityRoleIdByName): _description_ + + Raises: + ArkServiceException: _description_ + + Returns: + str: _description_ + """ + self._logger.info(f'Retrieving role id for name [{role_id_by_name.role_name}]') + directories_service = ArkIdentityDirectoriesService(self._isp_auth) + directories = [ + d.directory_service_uuid + for d in directories_service.list_directories(ArkIdentityListDirectories(directories=[DirectoryService.Identity])) + ] + response: Response = self._client.post( + f'{self._url_prefix}{DIRECTORY_SERVICE_QUERY_URL}', + data=DirectoryServiceQuerySpecificRoleRequest( + role_name=role_id_by_name.role_name, directory_services=directories, args=DirectorySearchArgs(limit=1) + ).json(by_alias=True, exclude={'users'}), + ) + if response.status_code != HTTPStatus.OK: + raise ArkServiceException(f'Failed to query for directory services role [{response.text}]') + try: + result = DirectoryServiceQueryResponse.parse_raw(response.text) + all_roles = result.result.roles.results + if not len(all_roles): + raise ArkServiceException('No role found for given name') + return all_roles[0].row.id + except (ValidationError, JSONDecodeError) as ex: + self._logger.exception(f'Failed to parse role id by name response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse role id by name response [{str(ex)}]') from ex + + def add_user_to_role(self, add_user_to_role: ArkIdentityAddUserToRole) -> None: + """ + Adds a given user to the role + + Args: + add_user_to_role (ArkIdentityAddUserToRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Adding user [{add_user_to_role.username}] to role [{add_user_to_role.role_name}]') + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=add_user_to_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{ADD_USER_TO_ROLE_URL}', + json={ + 'Name': role_id, + 'Users': [add_user_to_role.username], + }, + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to add user to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse add user to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse add user to role response [{str(ex)}]') from ex + + def add_group_to_role(self, add_group_to_role: ArkIdentityAddGroupToRole) -> None: + """ + Adds a given group to the role + + Args: + add_group_to_role (ArkIdentityAddGroupToRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Adding group [{add_group_to_role.group_name}] to role [{add_group_to_role.role_name}]') + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=add_group_to_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{ADD_USER_TO_ROLE_URL}', + json={ + 'Name': role_id, + 'Groups': [add_group_to_role.group_name], + }, + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to add group to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse add group to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse add group to role response [{str(ex)}]') from ex + + def add_role_to_role(self, add_role_to_role: ArkIdentityAddRoleToRole) -> None: + """ + Adds a given group to the role + + Args: + add_role_to_role (ArkIdentityAddRoleToRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Adding role [{add_role_to_role.role_name_to_add}] to role [{add_role_to_role.role_name}]') + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=add_role_to_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{ADD_USER_TO_ROLE_URL}', + json={ + 'Name': role_id, + 'Roles': [add_role_to_role.role_name_to_add], + }, + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to add role to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse add role to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse add role to role response [{str(ex)}]') from ex + + def remove_user_from_role(self, remove_user_from_role: ArkIdentityRemoveUserFromRole) -> None: + """ + Removes a given user from the given role + + Args: + remove_user_from_role (ArkIdentityRemoveUserFromRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Removing user [{remove_user_from_role.username}] from role [{remove_user_from_role.role_name}]') + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=remove_user_from_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{REMOVE_USER_FROM_ROLE_URL}', json={'Name': role_id, 'Users': [remove_user_from_role.username]} + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to remove user to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse remove user to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse remove user to role response [{str(ex)}]') from ex + + def remove_group_from_role(self, remove_group_from_role: ArkIdentityRemoveGroupFromRole) -> None: + """ + Removes a given group from the given role + + Args: + remove_group_from_role (ArkIdentityRemoveGroupFromRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Removing group [{remove_group_from_role.group_name}] from role [{remove_group_from_role.role_name}]') + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=remove_group_from_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{REMOVE_USER_FROM_ROLE_URL}', json={'Name': role_id, 'Groups': [remove_group_from_role.group_name]} + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to remove group to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse remove group to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse remove group to role response [{str(ex)}]') from ex + + def remove_role_from_role(self, remove_role_from_role: ArkIdentityRemoveRoleFromRole) -> None: + """ + Removes a given role from the given role + + Args: + remove_role_from_role (ArkIdentityRemoveRoleFromRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Removing group [{remove_role_from_role.role_name}] from role [{remove_role_from_role.role_name_to_remove}]') + role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=remove_role_from_role.role_name)) + response: Response = self._client.post( + f'{self._url_prefix}{REMOVE_USER_FROM_ROLE_URL}', json={'Name': role_id, 'Roles': [remove_role_from_role.role_name_to_remove]} + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to remove role to role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse remove role to role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse remove role to role response [{str(ex)}]') from ex + + def delete_role(self, delete_role: ArkIdentityDeleteRole) -> None: + """ + Deletes a given role by name + + Args: + delete_role (ArkIdentityDeleteRole): _description_ + + Raises: + ArkServiceException: _description_ + """ + self._logger.info(f'Deleting role [{delete_role.role_name}]') + if delete_role.role_name and not delete_role.role_id: + delete_role.role_id = self.role_id_by_name(ArkIdentityRoleIdByName(role_name=delete_role.role_name)) + response: Response = self._client.post(f'{self._url_prefix}{DELETE_ROLE_URL}', json={'Name': delete_role.role_id}) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to delete role [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse delete role response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse delete role response [{str(ex)}]') from ex + + @staticmethod + @overrides + def service_config() -> ArkServiceConfig: + return SERVICE_CONFIG diff --git a/ark_sdk_python/services/identity/users/__init__.py b/ark_sdk_python/services/identity/users/__init__.py new file mode 100644 index 00000000..f9573045 --- /dev/null +++ b/ark_sdk_python/services/identity/users/__init__.py @@ -0,0 +1,3 @@ +from ark_sdk_python.services.identity.users.ark_identity_users_service import ArkIdentityUsersService + +__all__ = ['ArkIdentityUsersService'] diff --git a/ark_sdk_python/services/identity/users/ark_identity_users_service.py b/ark_sdk_python/services/identity/users/ark_identity_users_service.py new file mode 100644 index 00000000..5a874c18 --- /dev/null +++ b/ark_sdk_python/services/identity/users/ark_identity_users_service.py @@ -0,0 +1,267 @@ +from datetime import datetime, timezone +from http import HTTPStatus +from typing import Final + +from overrides import overrides +from pydantic.error_wrappers import ValidationError +from requests import Response +from requests.exceptions import JSONDecodeError + +from ark_sdk_python.models.ark_exceptions import ArkServiceException +from ark_sdk_python.models.services import ArkServiceConfig +from ark_sdk_python.models.services.identity.roles import ArkIdentityAddUserToRole +from ark_sdk_python.models.services.identity.users import ( + ArkIdentityCreateUser, + ArkIdentityDeleteUser, + ArkIdentityResetUserPassword, + ArkIdentityUpdateUser, + ArkIdentityUser, + ArkIdentityUserByName, + ArkIdentityUserIdByName, + ArkIdentityUserInfo, +) +from ark_sdk_python.services.identity.common import ArkIdentityBaseService +from ark_sdk_python.services.identity.directories import ArkIdentityDirectoriesService +from ark_sdk_python.services.identity.roles import ArkIdentityRolesService + +SERVICE_CONFIG: Final[ArkServiceConfig] = ArkServiceConfig( + service_name='identity-users', required_authenticator_names=['isp'], optional_authenticator_names=[] +) + +TENANT_SUFFIX_URL: Final[str] = 'Core/GetCdsAliasesForTenant' +CREATE_USER_URL: Final[str] = 'CDirectoryService/CreateUser' +UPDATE_USER_URL: Final[str] = 'CDirectoryService/ChangeUser' +DELETE_USER_URL: Final[str] = 'UserMgmt/RemoveUsers' +RESET_USER_PASSWORD_URL: Final[str] = 'UserMgmt/ResetUserPassword' +REDROCK_QUERY: Final[str] = 'Redrock/query' +USER_INFO_URL: Final[str] = 'OAuth2/UserInfo/__idaptive_cybr_user_oidc' + + +class ArkIdentityUsersService(ArkIdentityBaseService): + def create_user(self, create_user: ArkIdentityCreateUser) -> ArkIdentityUser: + """ + Creates a user with the given details, and returns its finalized details and id + + Args: + create_user (ArkIdentityCreateUser): _description_ + + Raises: + ArkServiceException: _description_ + + Returns: + ArkIdentityUser: _description_ + """ + self._logger.info(f'Creating identity user [{create_user.username}]') + directories_service = ArkIdentityDirectoriesService(self._isp_auth) + tenant_suffix = create_user.suffix or directories_service.tenant_default_suffix() + response: Response = self._client.post( + f'{self._url_prefix}{CREATE_USER_URL}', + json={ + "DisplayName": create_user.display_name, + "Name": f'{create_user.username}@{tenant_suffix}', + "Mail": create_user.email, + "Password": create_user.password.get_secret_value(), + "MobileNumber": create_user.mobile_number, + "InEverybodyRole": 'true', + "InSysAdminRole": 'false', + "ForcePasswordChangeNext": 'false', + "SendEmailInvite": 'false', + "SendSmsInvite": 'false', + }, + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to create user [{response.text}]') + if create_user.roles: + roles_service = ArkIdentityRolesService(self._isp_auth) + for role in create_user.roles: + roles_service.add_user_to_role( + ArkIdentityAddUserToRole(username=f'{create_user.username}@{tenant_suffix}', role_name=role) + ) + self._logger.info(f'User created successfully with id [{result["Result"]}]') + return ArkIdentityUser( + user_id=result['Result'], + username=f'{create_user.username}@{tenant_suffix}', + display_name=create_user.display_name, + email=create_user.email, + mobile_number=create_user.mobile_number, + roles=create_user.roles, + ) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse create user response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse create user response [{str(ex)}]') from ex + + def update_user(self, update_user: ArkIdentityUpdateUser) -> None: + """ + Updates the user information + + Args: + update_user (ArkIdentityUpdateUser): _description_ + + Raises: + ArkServiceException: _description_ + """ + if update_user.username and not update_user.user_id: + update_user.user_id = self.user_id_by_name(ArkIdentityUserIdByName(username=update_user.username)) + self._logger.info(f'Updating identity user [{update_user.user_id}]') + update_dict = {} + if update_user.new_username: + if '@' not in update_user.new_username: + tenant_suffix = update_user.username.split('@')[1] + update_user.new_username = f'{update_user.new_username}@{tenant_suffix}' + update_dict['Name'] = update_user.new_username + if update_user.display_name: + update_dict['DisplayName'] = update_user.display_name + if update_user.email: + update_dict['Mail'] = update_user.email + if update_user.mobile_number: + update_dict['MobileNumber'] = update_user.mobile_number + update_dict['ID'] = update_user.user_id + response: Response = self._client.post(f'{self._url_prefix}{UPDATE_USER_URL}', json=update_dict) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to update user [{response.text}]') + self._logger.info('User updated successfully') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse update user response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse update user response [{str(ex)}]') from ex + + def delete_user(self, delete_user: ArkIdentityDeleteUser) -> None: + """ + Deletes a user by given name + + Args: + delete_user (ArkIdentityDeleteUser): _description_ + + Raises: + ArkServiceException: _description_ + """ + if delete_user.username and not delete_user.user_id: + delete_user.user_id = self.user_id_by_name(ArkIdentityUserIdByName(username=delete_user.username)) + self._logger.info(f'Deleting user [{delete_user.user_id}]') + response: Response = self._client.post(f'{self._url_prefix}{DELETE_USER_URL}', json={'Users': [delete_user.user_id]}) + try: + if response.status_code != HTTPStatus.OK or not response.json()['success']: + raise ArkServiceException(f'Failed to delete user [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse delete user response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse delete user response [{str(ex)}]') from ex + + def user_id_by_name(self, user_id_by_name: ArkIdentityUserIdByName) -> str: + """ + Finds the identifier of the given username + + Args: + user_id_by_name (ArkIdentityUserIdByName): _description_ + + Returns: + str: _description_ + """ + response: Response = self._client.post( + f'{self._url_prefix}{REDROCK_QUERY}', + json={"Script": f"Select ID, Username from User WHERE Username='{user_id_by_name.username}'"}, + ) + if response.status_code != HTTPStatus.OK: + raise ArkServiceException(f'Failed to retrieve user id by name [{response.text}] - [{response.status_code}]') + try: + query_result = response.json() + if not query_result['success'] or len(query_result['Result']["Results"]) == 0: + raise ArkServiceException('Failed to retrieve user id by name') + return query_result['Result']["Results"][0]["Row"]["ID"] + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse user id by name response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse user id by name response [{str(ex)}]') from ex + + def user_by_name(self, user_id_by_name: ArkIdentityUserByName) -> ArkIdentityUser: + """ + Finds the identifier of the given username + + Args: + user_id_by_name (ArkIdentityUserIdByName): _description_ + + Returns: + str: _description_ + """ + response: Response = self._client.post( + f'{self._url_prefix}{REDROCK_QUERY}', + json={ + "Script": f"Select ID, Username, DisplayName, Email, MobileNumber, LastLogin from User WHERE Username='{user_id_by_name.username}'" + }, + ) + if response.status_code != HTTPStatus.OK: + raise ArkServiceException(f'Failed to retrieve user id by name [{response.text}] - [{response.status_code}]') + try: + query_result = response.json() + if not query_result['success'] or len(query_result['Result']["Results"]) == 0: + raise ArkServiceException('Failed to retrieve user id by name') + user_row = query_result['Result']["Results"][0]["Row"] + last_login = None + if last_login := user_row.get('LastLogin'): + try: + last_login = last_login.split('(')[1].split(')')[0] + last_login = f'{last_login[:10]}.{last_login[10:]}' # for milliseconds + last_login = datetime.fromtimestamp(float(last_login), timezone.utc) + except Exception as ex: + self._logger.debug(f'Failed to parse last login [{user_row.get("LastLogin")}] [{str(ex)}]') + + return ArkIdentityUser( + user_id=user_row["ID"], + username=user_row["Username"], + display_name=user_row["DisplayName"], + email=user_row["Email"], + mobile_number=user_row["MobileNumber"], + last_login=last_login, + ) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse user id by name response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse user id by name response [{str(ex)}]') from ex + + def reset_user_password(self, reset_user_password: ArkIdentityResetUserPassword) -> None: + """ + Resets a given username's password to the new given one + Assumes the logged in user has permissions to do so + + Args: + reset_user_password (ArkIdentityResetUserPassword): _description_ + + Raises: + ArkServiceException: _description_ + """ + user_id = self.user_id_by_name(ArkIdentityUserIdByName(username=reset_user_password.username)) + response: Response = self._client.post( + f'{self._url_prefix}{RESET_USER_PASSWORD_URL}', json={'ID': user_id, 'newPassword': reset_user_password.new_password} + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK or not result['success']: + raise ArkServiceException(f'Failed to reset user password [{response.text}]') + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to parse reset user password response [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to parse reset user password response [{str(ex)}]') from ex + + def user_info(self) -> ArkIdentityUserInfo: + """ + Retrieves the current user info + + Raises: + ArkServiceException: _description_ + """ + response: Response = self._client.post( + f'{self._url_prefix}{USER_INFO_URL}', + json={'Scopes': ['userInfo']}, + ) + try: + result = response.json() + if response.status_code != HTTPStatus.OK: + raise ArkServiceException(f'Failed to get user info [{response.text}]') + return ArkIdentityUserInfo.parse_obj(result) + except (ValidationError, JSONDecodeError, KeyError) as ex: + self._logger.exception(f'Failed to get user info [{str(ex)}] - [{response.text}]') + raise ArkServiceException(f'Failed to get user info [{str(ex)}]') from ex + + @staticmethod + @overrides + def service_config() -> ArkServiceConfig: + return SERVICE_CONFIG diff --git a/docs/commands/exec.md b/docs/commands/exec.md index ddb5df6b..f9b0e213 100644 --- a/docs/commands/exec.md +++ b/docs/commands/exec.md @@ -11,7 +11,7 @@ You use the `exec` command is used to run commands on available services (the av The following DPA commands are supported: -- `ark exec dpa`: Root command for the DBA service +- `ark exec dpa`: Root command for the DPA service - `policies` - Policy management - `db` - DB policies - `editor` - DB policies interactive editor @@ -25,6 +25,12 @@ The following DPA commands are supported: - `workspaces` - Workspaces management - `db` - DB workspace management - `k8s` - Kubernetes service +- `ark exec sm`: Root command for Session Monitoring service +- `ark exec identity`: Root command for Identity service + - `roles` - Roles management + - `users` - Users management + - `policies` - Policies management + - `directories` - Directories reading All commands have their own subcommands and respective arguments. @@ -38,10 +44,10 @@ ark exec usage: ark exec [-h] [-r] [-s] [-ao] [-v] [-ls {default}] [-ll {DEBUG,INFO,WARN,ERROR,CRITICAL}] [-dcv] [-tc TRUSTED_CERT] [-pn PROFILE_NAME] [-op OUTPUT_PATH] [-rf REQUEST_FILE] [-rc RETRY_COUNT] [-ra] - {dpa} ... + {identity,dpa,sm} ... positional arguments: - {dpa} + {identity,dpa,sm} optional arguments: -h, --help show this help message and exit diff --git a/docs/examples/commands_examples.md b/docs/examples/commands_examples.md index bc45faa4..e4eff3f6 100644 --- a/docs/examples/commands_examples.md +++ b/docs/examples/commands_examples.md @@ -164,3 +164,43 @@ ark exec sm count-session-activities-by --session-id 5e62bdb8-cd81-42b8-ac72-1e0 ```shell ark exec sm sessions-stats ``` + +### List all identity entities, including roles users and groups +```shell +ark exec identity directories list-directories-entities +``` + +### List only identity roles +```shell +ark exec identity directories list-directories-entities --entity-types ROLE +``` + +### Create a role with DPA show tile admin right +```shell +ark exec identity roles create-role --role-name RoleName --admin-rights "ServiceRight/dpaShowTile" +``` + +### Delete a role by name +```shell +ark exec identity roles delete-role --role-name RoleName +``` + +### Create a new user +```shell +ark exec identity users create-user --username myname --email email@email.com --password MyPassword +``` + +### Delete a user by name +```shell +ark exec identity users delete-user --username myname +``` + +### Add an authentication profile +```shell +ark exec identity policies add-authentication-profile --auth-profile-name myprofile --first-challenges UP --second-challenges EMAIL +``` + +### Add a policy +```shell +ark exec identity policies add-policy --policy-name mypolicy --role-names RoleName --auth-profile-name myprofile +``` diff --git a/docs/examples/sdk_examples.md b/docs/examples/sdk_examples.md index e8a05c77..107b12bd 100644 --- a/docs/examples/sdk_examples.md +++ b/docs/examples/sdk_examples.md @@ -102,77 +102,94 @@ if __name__ == '__main__': ## Authenticate and provision DPA VM policies ```python +isp_auth = ArkISPAuth() +isp_auth.authenticate( + auth_profile=ArkAuthProfile( + username=username, auth_method=ArkAuthMethod.Identity, auth_method_settings=IdentityArkAuthMethodSettings() + ), + secret=ArkSecret(secret='CoolPassword'), +) +print('Adding DPA Policy') +dpa_service.policies.add_policy( + ArkDPAVMAddPolicy( + policy_name='IT Policy', + description='IT Policy', + status=ArkDPARuleStatus.Enabled, + providers_data={ + ArkWorkspaceType.AWS: ArkDPAVMAWSProviderData( + account_ids=['965428623928'], tags=[{'key': 'team', 'value': 'IT'}], regions=[], vpc_ids=[] + ) + }, + user_access_rules=[ + ArkDPAVMAuthorizationRule( + rule_name='IT Rule', + user_data=ArkDPAUserData(roles=['IT']), + connection_information=ArkDPAVMConnectionInformation( + full_days=True, + days_of_week=[], + time_zone='Asia/Jerusalem', + connect_as={ + ArkWorkspaceType.AWS: { + ArkProtocolType.SSH: 'root', + ArkProtocolType.RDP: ArkDPAVMRDPLocalEphemeralUserConnectionData( + local_ephemeral_user=ArkDPAVMLocalEphemeralUserConnectionMethodData(assign_groups={'Administrators'}) + ), + } + }, + ), + ) + ], + ) +) +``` + +## View Session Monitoring Sessions And Activities Per Session + +```python +from ark_sdk_python.services.sm import ArkSMService +from ark_sdk_python.models.services.sm import ArkSMSessionsFilter, ArkSMGetSession, ArkSMGetSessionActivities +from ark_sdk_python.models.ark_profile import ArkProfileLoader +from ark_sdk_python.models.common import ArkProtocolType +from ark_sdk_python.auth import ArkISPAuth +from datetime import datetime, timedelta +if __name__ == "__main__": isp_auth = ArkISPAuth() isp_auth.authenticate( - auth_profile=ArkAuthProfile( - username=username, auth_method=ArkAuthMethod.Identity, auth_method_settings=IdentityArkAuthMethodSettings() - ), - secret=ArkSecret(secret='CoolPassword'), + profile=ArkProfileLoader().load_default_profile() ) - print('Adding DPA Policy') - dpa_service.policies.add_policy( - ArkDPAVMAddPolicy( - policy_name='IT Policy', - description='IT Policy', - status=ArkDPARuleStatus.Enabled, - providers_data={ - ArkWorkspaceType.AWS: ArkDPAVMAWSProviderData( - account_ids=['965428623928'], tags=[{'key': 'team', 'value': 'IT'}], regions=[], vpc_ids=[] - ) - }, - user_access_rules=[ - ArkDPAVMAuthorizationRule( - rule_name='IT Rule', - user_data=ArkDPAUserData(roles=['IT']), - connection_information=ArkDPAVMConnectionInformation( - full_days=True, - days_of_week=[], - time_zone='Asia/Jerusalem', - connect_as={ - ArkWorkspaceType.AWS: { - ArkProtocolType.SSH: 'root', - ArkProtocolType.RDP: ArkDPAVMRDPLocalEphemeralUserConnectionData( - local_ephemeral_user=ArkDPAVMLocalEphemeralUserConnectionMethodData(assign_groups={'Administrators'}) - ), - } - }, - ), - ) - ], - ) + sm: ArkSMService = ArkSMService(isp_auth) + search_by = 'startTime ge {start_time_from} AND sessionDuration GE {min_duration} AND protocol IN {protocols}' + search_by = search_by.format( + start_time_from=(datetime.utcnow() - timedelta(days=30)).isoformat(timespec='seconds'), + min_duration='00:00:01', + protocols=','.join([ArkProtocolType.DB[0], ArkProtocolType.SSH[0], ArkProtocolType.RDP[0]]), + ) + sessions_filter = ArkSMSessionsFilter( + search=search_by, ) + print(f'session_count = {sm.count_sessions_by(sessions_filter)}') + for s_page in sm.list_sessions_by(sessions_filter): + for session in s_page.items: + session = sm.session(ArkSMGetSession(session_id=session.session_id)) + get_session_activities = ArkSMGetSessionActivities(session_id=session.session_id) + print(f'session = {session}, activities_count = {sm.count_session_activities(get_session_activities)}') + session_activities = [activity for page in sm.list_session_activities(get_session_activities) for activity in page.items] + print(session_activities) ``` -## View Session Monitoring Sessions And Activities Per Session +## Get current tenant default suffix ```python - from ark_sdk_python.services.sm import ArkSMService - from ark_sdk_python.models.services.sm import ArkSMSessionsFilter, ArkSMGetSession, ArkSMGetSessionActivities - from ark_sdk_python.models.ark_profile import ArkProfileLoader - from ark_sdk_python.models.common import ArkProtocolType - from ark_sdk_python.auth import ArkISPAuth - from datetime import datetime, timedelta - if __name__ == "__main__": - isp_auth = ArkISPAuth() - isp_auth.authenticate( - profile=ArkProfileLoader().load_default_profile() - ) - sm: ArkSMService = ArkSMService(isp_auth) - search_by = 'startTime ge {start_time_from} AND sessionDuration GE {min_duration} AND protocol IN {protocols}' - search_by = search_by.format( - start_time_from=(datetime.utcnow() - timedelta(days=30)).isoformat(timespec='seconds'), - min_duration='00:00:01', - protocols=','.join([ArkProtocolType.DB[0], ArkProtocolType.SSH[0], ArkProtocolType.RDP[0]]), - ) - sessions_filter = ArkSMSessionsFilter( - search=search_by, - ) - print(f'session_count = {sm.count_sessions_by(sessions_filter)}') - for s_page in sm.list_sessions_by(sessions_filter): - for session in s_page.items: - session = sm.session(ArkSMGetSession(session_id=session.session_id)) - get_session_activities = ArkSMGetSessionActivities(session_id=session.session_id) - print(f'session = {session}, activities_count = {sm.count_session_activities(get_session_activities)}') - session_activities = [activity for page in sm.list_session_activities(get_session_activities) for activity in page.items] - print(session_activities) +from ark_sdk_python.auth import ArkISPAuth +from ark_sdk_python.models.ark_profile import ArkProfileLoader +from ark_sdk_python.models.services.identity.directories import ArkIdentityListDirectoriesEntities +from ark_sdk_python.services.identity import ArkIdentityAPI + +if __name__ == "__main__": + isp_auth = ArkISPAuth() + isp_auth.authenticate(ArkProfileLoader().load_default_profile()) + identity_api = ArkIdentityAPI(isp_auth) + print(identity_api.identity_directories.tenant_default_suffix()) + for page in identity_api.identity_directories.list_directories_entities(ArkIdentityListDirectoriesEntities()): + print([i.name for i in page.items]) ``` diff --git a/pyproject.toml b/pyproject.toml index 9e73d909..054e2b00 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "ark-sdk-python" -version = "1.0.7" +version = "1.1.0" description='Official Ark SDK / CLI for CyberArk Identity Security Platform' authors = ["CyberArk ", "Ofir Iluz Date: Thu, 4 Apr 2024 11:13:50 +0300 Subject: [PATCH 2/6] Minor fix to defaults --- .../services/dpa/policies/db/ark_dpa_db_connection_data.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py b/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py index 8ea5b760..dd1fbde7 100644 --- a/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py +++ b/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py @@ -40,9 +40,9 @@ class ArkDPADBOracleDBAuth(ArkDPADBBaseAuth): class ArkDPADBMongoDBAuth(ArkDPADBBaseAuth): - global_builtin_roles: List[ArkDPADBMongoGlobalBuiltinRole] = Field(description='Global builtin roles across all databases') - database_builtin_roles: Dict[str, List[ArkDPADBMongoDatabaseBuiltinRole]] = Field(description='Per database builtin roles') - database_custom_roles: Dict[str, List[str]] = Field(description='Custom per database roles') + global_builtin_roles: List[ArkDPADBMongoGlobalBuiltinRole] = Field(description='Global builtin roles across all databases', default_factory=list) + database_builtin_roles: Dict[str, List[ArkDPADBMongoDatabaseBuiltinRole]] = Field(description='Per database builtin roles', default_factory=dict) + database_custom_roles: Dict[str, List[str]] = Field(description='Custom per database roles', default_factory=dict) applied_to: Optional[List[ArkDPADBAppliedTo]] = Field(description='Which resources to apply to') From 0b959da4884d50bc0f3fac2011917d50b462658d Mon Sep 17 00:00:00 2001 From: Ofir Iluz Date: Thu, 4 Apr 2024 11:27:07 +0300 Subject: [PATCH 3/6] Minor fix to defaults --- .../dpa/policies/db/ark_dpa_db_connection_data.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py b/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py index dd1fbde7..8ad49847 100644 --- a/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py +++ b/ark_sdk_python/models/services/dpa/policies/db/ark_dpa_db_connection_data.py @@ -40,8 +40,12 @@ class ArkDPADBOracleDBAuth(ArkDPADBBaseAuth): class ArkDPADBMongoDBAuth(ArkDPADBBaseAuth): - global_builtin_roles: List[ArkDPADBMongoGlobalBuiltinRole] = Field(description='Global builtin roles across all databases', default_factory=list) - database_builtin_roles: Dict[str, List[ArkDPADBMongoDatabaseBuiltinRole]] = Field(description='Per database builtin roles', default_factory=dict) + global_builtin_roles: List[ArkDPADBMongoGlobalBuiltinRole] = Field( + description='Global builtin roles across all databases', default_factory=list + ) + database_builtin_roles: Dict[str, List[ArkDPADBMongoDatabaseBuiltinRole]] = Field( + description='Per database builtin roles', default_factory=dict + ) database_custom_roles: Dict[str, List[str]] = Field(description='Custom per database roles', default_factory=dict) applied_to: Optional[List[ArkDPADBAppliedTo]] = Field(description='Which resources to apply to') From 2c21fd3ba8252db9d86cf64af0491c120d166148 Mon Sep 17 00:00:00 2001 From: Ofir Iluz Date: Sat, 6 Apr 2024 19:17:36 +0300 Subject: [PATCH 4/6] Minor fixes --- .../dpa/workspaces/db/ark_dpa_db_add_database.py | 1 + .../dpa/workspaces/db/ark_dpa_db_database.py | 1 + .../workspaces/db/ark_dpa_db_update_database.py | 1 + .../services/dpa/sso/ark_dpa_sso_service.py | 14 +++++++++----- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_add_database.py b/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_add_database.py index bd1589c1..743a265f 100644 --- a/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_add_database.py +++ b/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_add_database.py @@ -14,6 +14,7 @@ class ArkDPADBAddDatabase(ArkCamelizedModel): platform: ArkWorkspaceType = Field( description='Platform of the database, as in, where it resides, defaulted to on premises', default=ArkWorkspaceType.ONPREM ) + auth_database: str = Field(description='Authentication database used, most commonly used with mongodb', default='admin') services: Optional[List[str]] = Field(description='Services related to the database, most commonly used with oracle') domain: Optional[str] = Field(description='The domain the DB resides in') domain_controller_name: Optional[str] = Field(description='Domain controller name associated to this database') diff --git a/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_database.py b/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_database.py index dbf14fe4..eed450d5 100644 --- a/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_database.py +++ b/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_database.py @@ -13,6 +13,7 @@ class ArkDPADBDatabase(ArkCamelizedModel): name: str = Field(description='Name of the database, often referenced in policies and other APIs') network_name: str = Field(description='Name of the network the database resides in', default='OnPrem') platform: Optional[ArkWorkspaceType] = Field(description='Platform of the database, as in, where it resides') + auth_database: str = Field(description='Authentication database used, most commonly used with mongodb', default='admin') services: List[str] = Field(description='Services related to the database, most commonly used with oracle', default_factory=list) domain: Optional[str] = Field(description='The domain the DB resides in') domain_controller_name: Optional[str] = Field(description='Domain controller name associated to this database') diff --git a/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_update_database.py b/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_update_database.py index 6a1b878a..4ed19ea9 100644 --- a/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_update_database.py +++ b/ark_sdk_python/models/services/dpa/workspaces/db/ark_dpa_db_update_database.py @@ -14,6 +14,7 @@ class ArkDPADBUpdateDatabase(ArkCamelizedModel): new_name: Optional[str] = Field(description='New name for the database') network_name: Optional[str] = Field(description='Name of the network the database resides in', default='ON-PREMISE') platform: Optional[ArkWorkspaceType] = Field(description='Platform of the database, as in, where it resides') + auth_database: str = Field(description='Authentication database used, most commonly used with mongodb', default='admin') services: Optional[List[str]] = Field(description='Services related to the database, most commonly used with oracle') domain: Optional[str] = Field(description='The domain the DB resides in') domain_controller_name: Optional[str] = Field(description='Domain controller name associated to this database') diff --git a/ark_sdk_python/services/dpa/sso/ark_dpa_sso_service.py b/ark_sdk_python/services/dpa/sso/ark_dpa_sso_service.py index 601ca64d..8b02f753 100644 --- a/ark_sdk_python/services/dpa/sso/ark_dpa_sso_service.py +++ b/ark_sdk_python/services/dpa/sso/ark_dpa_sso_service.py @@ -77,6 +77,7 @@ def __output_client_certificate( ) -> None: folder_path = self.__expand_folder(folder) claims = get_unverified_claims(self.__client.session_token) + base_name = claims["unique_name"].split('@')[0] client_certificate = result.token['client_certificate'] private_key = result.token['private_key'] @@ -95,9 +96,9 @@ def __output_client_certificate( ) if not os.path.exists(folder_path): os.makedirs(folder_path) - with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}client_cert.crt', 'w', encoding='utf-8') as file_handle: + with open(f'{folder_path}{os.path.sep}{base_name}_client_cert.crt', 'w', encoding='utf-8') as file_handle: file_handle.write(client_certificate) - with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}client_key.pem', 'w', encoding='utf-8') as file_handle: + with open(f'{folder_path}{os.path.sep}{base_name}_client_key.pem', 'w', encoding='utf-8') as file_handle: file_handle.write(private_key) elif output_format == ArkDPASSOShortLiveClientCertificateFormat.SINGLE_FILE: if not folder: @@ -106,7 +107,7 @@ def __output_client_certificate( ) if not os.path.exists(folder_path): os.makedirs(folder_path) - with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}client_cert.pem', 'w', encoding='utf-8') as file_handle: + with open(f'{folder_path}{os.path.sep}{base_name}_client_cert.pem', 'w', encoding='utf-8') as file_handle: file_handle.write(client_certificate) file_handle.write('\n') file_handle.write(private_key) @@ -119,7 +120,9 @@ def __save_oracle_sso_wallet(self, folder: str, unzip_wallet: bool, result: ArkD if not os.path.exists(folder_path): os.makedirs(folder_path) if not unzip_wallet: - with open(f'{folder_path}{os.path.sep}wallet.zip', 'wb') as file_handle: + claims = get_unverified_claims(self.__client.session_token) + base_name = claims["unique_name"].split('@')[0] + with open(f'{folder_path}{os.path.sep}{base_name}_wallet.zip', 'wb') as file_handle: file_handle.write(result.token['wallet']) else: wallet_bytes = BytesIO(result.token['wallet']) @@ -129,10 +132,11 @@ def __save_oracle_sso_wallet(self, folder: str, unzip_wallet: bool, result: ArkD def __save_oracle_pem_wallet(self, folder: str, result: ArkDPASSOAcquireTokenResponse) -> None: folder_path = self.__expand_folder(folder) claims = get_unverified_claims(self.__client.session_token) + base_name = claims["unique_name"].split('@')[0] pem_wallet = base64.b64decode(result.token['pem_wallet']).decode('utf-8') if not os.path.exists(folder_path): os.makedirs(folder_path) - with open(f'{folder_path}{os.path.sep}{claims["unique_name"]}ewallet.pem', 'w', encoding='utf-8') as file_handle: + with open(f'{folder_path}{os.path.sep}{base_name}_ewallet.pem', 'w', encoding='utf-8') as file_handle: file_handle.write(pem_wallet) def __save_rdp_file(self, get_short_lived_rdp_file: ArkDPASSOGetShortLivedRDPFile, result: ArkDPASSOAcquireTokenResponse) -> None: From 501e4fad13ca853fb3141d2451a0db1614b7cc09 Mon Sep 17 00:00:00 2001 From: Ofir Iluz Date: Sat, 6 Apr 2024 19:43:41 +0300 Subject: [PATCH 5/6] Some more examples --- .../examples/create_identity_resources.py | 20 ++++++++++++ docs/examples/sdk_examples.md | 32 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 ark_sdk_python/examples/create_identity_resources.py diff --git a/ark_sdk_python/examples/create_identity_resources.py b/ark_sdk_python/examples/create_identity_resources.py new file mode 100644 index 00000000..8635b8f5 --- /dev/null +++ b/ark_sdk_python/examples/create_identity_resources.py @@ -0,0 +1,20 @@ +from ark_sdk_python.auth import ArkISPAuth +from ark_sdk_python.models.auth import ArkAuthMethod, ArkAuthProfile, ArkSecret, IdentityArkAuthMethodSettings +from ark_sdk_python.models.services.identity.roles import ArkIdentityCreateRole +from ark_sdk_python.models.services.identity.users import ArkIdentityCreateUser +from ark_sdk_python.services.identity import ArkIdentityAPI + +if __name__ == "__main__": + isp_auth = ArkISPAuth() + isp_auth.authenticate( + auth_profile=ArkAuthProfile( + username='CoolUser', auth_method=ArkAuthMethod.Identity, auth_method_settings=IdentityArkAuthMethodSettings() + ), + secret=ArkSecret(secret='CoolPassword'), + ) + + # Create an identity service to create some users and roles + print('Creating identity roles and users') + identity_api = ArkIdentityAPI(isp_auth) + identity_api.identity_roles.create_role(ArkIdentityCreateRole(role_name='IT')) + identity_api.identity_users.create_user(ArkIdentityCreateUser(username='it_user', password='CoolPassword', roles=['IT'])) diff --git a/docs/examples/sdk_examples.md b/docs/examples/sdk_examples.md index 107b12bd..bc230d5d 100644 --- a/docs/examples/sdk_examples.md +++ b/docs/examples/sdk_examples.md @@ -152,6 +152,7 @@ from ark_sdk_python.models.ark_profile import ArkProfileLoader from ark_sdk_python.models.common import ArkProtocolType from ark_sdk_python.auth import ArkISPAuth from datetime import datetime, timedelta + if __name__ == "__main__": isp_auth = ArkISPAuth() isp_auth.authenticate( @@ -193,3 +194,34 @@ if __name__ == "__main__": for page in identity_api.identity_directories.list_directories_entities(ArkIdentityListDirectoriesEntities()): print([i.name for i in page.items]) ``` + + +## Add identity role and user + +```python +from ark_sdk_python.models.auth import ( + ArkAuthMethod, + ArkAuthProfile, + ArkSecret, + IdentityArkAuthMethodSettings, +) +from ark_sdk_python.auth import ArkISPAuth +from ark_sdk_python.services.identity import ArkIdentityAPI +from ark_sdk_python.models.services.identity.roles import ArkIdentityCreateRole +from ark_sdk_python.models.services.identity.users import ArkIdentityCreateUser + +if __name__ == "__main__": + isp_auth = ArkISPAuth() + isp_auth.authenticate( + auth_profile=ArkAuthProfile( + username='CoolUser', auth_method=ArkAuthMethod.Identity, auth_method_settings=IdentityArkAuthMethodSettings() + ), + secret=ArkSecret(secret='CoolPassword'), + ) + + # Create an identity service to create some users and roles + print('Creating identity roles and users') + identity_api = ArkIdentityAPI(isp_auth) + identity_api.identity_roles.create_role(ArkIdentityCreateRole(role_name='IT')) + identity_api.identity_users.create_user(ArkIdentityCreateUser(username='it_user', password='CoolPassword', roles=['IT'])) +``` From ab1580382335af495319859e57d0dd11ad40e211 Mon Sep 17 00:00:00 2001 From: Ofir Iluz Date: Sat, 6 Apr 2024 19:46:44 +0300 Subject: [PATCH 6/6] Some more examples --- .../models/common/identity/ark_identity_directory_schemas.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ark_sdk_python/models/common/identity/ark_identity_directory_schemas.py b/ark_sdk_python/models/common/identity/ark_identity_directory_schemas.py index 87420b91..65820f7b 100644 --- a/ark_sdk_python/models/common/identity/ark_identity_directory_schemas.py +++ b/ark_sdk_python/models/common/identity/ark_identity_directory_schemas.py @@ -94,6 +94,7 @@ class RoleRow(ArkModel): id: str = Field(alias='_ID') admin_rights: Optional[List[RoleAdminRight]] = Field(alias='AdministrativeRights') is_hidden: Optional[bool] = Field(alias='IsHidden') + description: Optional[str] = Field(alias='Description') class RoleResult(ArkModel): @@ -113,6 +114,7 @@ class UserRow(ArkModel): directory_service_type: DirectoryService = Field(alias='ServiceType') email: Optional[str] = Field(alias='EMail') internal_id: Optional[str] = Field(alias='InternalName') + description: Optional[str] = Field(alias='Description') class UserResult(ArkModel):