scalekit-sdk-python 2.2.2__tar.gz → 2.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {scalekit_sdk_python-2.2.2/scalekit_sdk_python.egg-info → scalekit_sdk_python-2.3.0}/PKG-INFO +1 -1
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/client.py +97 -21
- scalekit_sdk_python-2.3.0/scalekit/common/exceptions.py +260 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/common/scalekit.py +15 -2
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/core.py +24 -37
- scalekit_sdk_python-2.3.0/scalekit/passwordless.py +145 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0/scalekit_sdk_python.egg-info}/PKG-INFO +1 -1
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit_sdk_python.egg-info/SOURCES.txt +2 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/setup.py +1 -1
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/LICENSE +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/README.md +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/expression_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/expression_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/expression_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/priv/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/priv/private_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/priv/private_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/priv/private_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/validate_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/validate_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/buf/validate/validate_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/common/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/common/user.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/connection.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/constants/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/constants/user.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/directory.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/domain.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/m2m_client.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/organization.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/role.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/users.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/utils/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/utils/directory.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auditlogs/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auditlogs/auditlogs_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auditlogs/auditlogs_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auditlogs/auditlogs_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/auth_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/auth_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/auth_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/passwordless_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/passwordless_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/auth/passwordless_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/clients/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/clients/clients_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/clients/clients_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/clients/clients_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/commons/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/commons/commons_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/commons/commons_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/commons/commons_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connected_accounts/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connected_accounts/connected_accounts_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connected_accounts/connected_accounts_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connected_accounts/connected_accounts_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connections/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connections/connections_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connections/connections_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/connections/connections_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/directories/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/directories/directories_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/directories/directories_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/directories/directories_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/domains/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/domains/domains_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/domains/domains_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/domains/domains_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/emails/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/emails/emails_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/emails/emails_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/emails/emails_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/environments/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/environments/environments_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/environments/environments_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/environments/environments_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/errdetails/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/errdetails/errdetails_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/errdetails/errdetails_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/errdetails/errdetails_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/events/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/events/events_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/events/events_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/events/events_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/invites/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/invites/invites_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/invites/invites_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/invites/invites_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/members/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/members/members_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/members/members_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/members/members_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/migrations/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/migrations/migrations_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/migrations/migrations_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/migrations/migrations_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/options/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/options/options_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/options/options_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/options/options_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/organizations/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/organizations/organizations_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/organizations/organizations_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/organizations/organizations_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/providers/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/providers/providers_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/providers/providers_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/providers/providers_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/roles/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/roles/roles_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/roles/roles_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/roles/roles_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/tools/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/tools/tools_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/tools/tools_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/tools/tools_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/user_attributes/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/user_attributes/user_attributes_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/user_attributes/user_attributes_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/user_attributes/user_attributes_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/users/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/users/users_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/users/users_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/users/users_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/webhooks/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/webhooks/webhooks_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/webhooks/webhooks_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/webhooks/webhooks_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/workspaces/__init__.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/workspaces/workspaces_pb2.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/workspaces/workspaces_pb2.pyi +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit/v1/workspaces/workspaces_pb2_grpc.py +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit_sdk_python.egg-info/dependency_links.txt +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit_sdk_python.egg-info/requires.txt +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/scalekit_sdk_python.egg-info/top_level.txt +0 -0
- {scalekit_sdk_python-2.2.2 → scalekit_sdk_python-2.3.0}/setup.cfg +0 -0
|
@@ -16,14 +16,18 @@ from scalekit.organization import OrganizationClient
|
|
|
16
16
|
from scalekit.directory import DirectoryClient
|
|
17
17
|
from scalekit.users import UserClient
|
|
18
18
|
from scalekit.role import RoleClient
|
|
19
|
+
from scalekit.passwordless import PasswordlessClient
|
|
19
20
|
from scalekit.common.scalekit import (
|
|
20
21
|
AuthorizationUrlOptions,
|
|
21
22
|
CodeAuthenticationOptions,
|
|
22
23
|
GrantType,
|
|
23
24
|
IdpInitiatedLoginClaims,
|
|
24
25
|
LogoutUrlOptions,
|
|
26
|
+
TokenValidationOptions,
|
|
25
27
|
)
|
|
26
28
|
from scalekit.constants.user import id_token_claim_to_user_map
|
|
29
|
+
from scalekit.common.exceptions import (WebhookVerificationError,
|
|
30
|
+
ScalekitValidateTokenFailureException)
|
|
27
31
|
|
|
28
32
|
AUTHORIZE_ENDPOINT = "oauth/authorize"
|
|
29
33
|
LOGOUT_ENDPOINT = "oidc/logout"
|
|
@@ -31,10 +35,6 @@ webhook_tolerance_in_seconds = timedelta(minutes=5)
|
|
|
31
35
|
webhook_signature_version = "v1"
|
|
32
36
|
|
|
33
37
|
|
|
34
|
-
class WebhookVerificationError(Exception):
|
|
35
|
-
pass
|
|
36
|
-
|
|
37
|
-
|
|
38
38
|
class ScalekitClient:
|
|
39
39
|
""" Class definition for scalekit client """
|
|
40
40
|
|
|
@@ -54,8 +54,7 @@ class ScalekitClient:
|
|
|
54
54
|
"""
|
|
55
55
|
try:
|
|
56
56
|
self.core_client = CoreClient(
|
|
57
|
-
env_url=env_url, client_id=client_id, client_secret=client_secret
|
|
58
|
-
)
|
|
57
|
+
env_url=env_url, client_id=client_id, client_secret=client_secret)
|
|
59
58
|
self.domain = DomainClient(self.core_client)
|
|
60
59
|
self.connection = ConnectionClient(self.core_client)
|
|
61
60
|
self.organization = OrganizationClient(self.core_client)
|
|
@@ -63,6 +62,7 @@ class ScalekitClient:
|
|
|
63
62
|
self.m2m_client = M2MClient(self.core_client)
|
|
64
63
|
self.users = UserClient(self.core_client)
|
|
65
64
|
self.roles = RoleClient(self.core_client)
|
|
65
|
+
self.passwordless = PasswordlessClient(self.core_client)
|
|
66
66
|
except Exception as exp:
|
|
67
67
|
raise exp
|
|
68
68
|
|
|
@@ -140,7 +140,7 @@ class ScalekitClient:
|
|
|
140
140
|
access_token = response["access_token"]
|
|
141
141
|
refresh_token = response.get("refresh_token")
|
|
142
142
|
# Validate id_token
|
|
143
|
-
claims = self.__validate_token(id_token,
|
|
143
|
+
claims = self.__validate_token(id_token, options=None)
|
|
144
144
|
user = {}
|
|
145
145
|
amr_claims = claims.get('amr', [])
|
|
146
146
|
connection_id = amr_claims[0] if amr_claims else None
|
|
@@ -158,21 +158,25 @@ class ScalekitClient:
|
|
|
158
158
|
"organization_id": organization_id
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
except jwt.exceptions.InvalidTokenError as exp:
|
|
162
|
+
raise ScalekitValidateTokenFailureException(exp)
|
|
161
163
|
except Exception as exp:
|
|
162
164
|
raise exp
|
|
163
165
|
|
|
164
|
-
def validate_access_token(self, token: str, options: Optional[
|
|
166
|
+
def validate_access_token(self, token: str, options: Optional[TokenValidationOptions] = None, audience = None) -> bool:
|
|
165
167
|
"""
|
|
166
168
|
Method to validate access token
|
|
167
169
|
|
|
168
170
|
:param token : access token
|
|
169
171
|
:type : ``` str ```
|
|
172
|
+
:param options : Optional validation options for issuer, audience, and scopes
|
|
173
|
+
:type : ``` TokenValidationOptions ```
|
|
174
|
+
:param audience : audience for validation (deprecated, use options.audience instead)
|
|
175
|
+
:type : ``` str ```
|
|
170
176
|
|
|
171
177
|
:returns:
|
|
172
178
|
bool
|
|
173
179
|
"""
|
|
174
|
-
options = options if options else {}
|
|
175
|
-
options["verify_aud"] = False if not audience else True
|
|
176
180
|
try:
|
|
177
181
|
self.__validate_token(token, options=options, audience=audience)
|
|
178
182
|
return True
|
|
@@ -205,59 +209,131 @@ class ScalekitClient:
|
|
|
205
209
|
except Exception as exp:
|
|
206
210
|
raise exp
|
|
207
211
|
|
|
208
|
-
def validate_access_token_and_get_claims(self, token: str, options: Optional[
|
|
212
|
+
def validate_access_token_and_get_claims(self, token: str, options: Optional[TokenValidationOptions] = None, audience = None) -> Dict[str, Any]:
|
|
209
213
|
"""
|
|
210
214
|
Method to validate access token and get claims
|
|
211
215
|
|
|
212
216
|
:param token : access token
|
|
213
217
|
:type : ``` str ```
|
|
218
|
+
:param options : Optional validation options for issuer, audience, and scopes
|
|
219
|
+
:type : ``` TokenValidationOptions ```
|
|
220
|
+
:param audience : audience for validation (deprecated, use options.audience instead)
|
|
221
|
+
:type : ``` str ```
|
|
214
222
|
|
|
215
223
|
:returns:
|
|
216
224
|
claims
|
|
217
225
|
"""
|
|
218
|
-
|
|
219
|
-
options = options if options else {}
|
|
220
|
-
options["verify_aud"] = False if not audience else True
|
|
221
226
|
|
|
222
227
|
try:
|
|
223
228
|
claims = self.__validate_token(token, options=options, audience=audience)
|
|
224
229
|
return claims
|
|
230
|
+
except jwt.exceptions.InvalidTokenError as exp:
|
|
231
|
+
raise ScalekitValidateTokenFailureException(exp)
|
|
225
232
|
except Exception as exp:
|
|
226
|
-
raise exp
|
|
233
|
+
raise exp
|
|
227
234
|
|
|
228
|
-
def get_idp_initiated_login_claims(self, idp_initiated_login_token: str) -> IdpInitiatedLoginClaims:
|
|
235
|
+
def get_idp_initiated_login_claims(self, idp_initiated_login_token: str, options: Optional[TokenValidationOptions] = None, audience = None) -> IdpInitiatedLoginClaims:
|
|
229
236
|
"""
|
|
230
237
|
Method to get IDP initiated login claims
|
|
231
238
|
|
|
232
239
|
:param idp_initiated_login_token : IDP initiated login token
|
|
233
240
|
:type : ``` str ```
|
|
241
|
+
:param options : Optional validation options for issuer and audience
|
|
242
|
+
:type : ``` TokenValidationOptions ```
|
|
243
|
+
:param audience : audience for validation (deprecated, use options.audience instead)
|
|
244
|
+
:type : ``` str ```
|
|
234
245
|
|
|
235
246
|
:returns:
|
|
236
247
|
``` IdpInitiatedLoginClaims ```
|
|
237
248
|
"""
|
|
238
249
|
try:
|
|
239
|
-
claims = self.__validate_token(idp_initiated_login_token,
|
|
250
|
+
claims = self.__validate_token(idp_initiated_login_token, options=options, audience=audience)
|
|
240
251
|
return claims
|
|
252
|
+
except jwt.exceptions.InvalidTokenError as exp:
|
|
253
|
+
raise ScalekitValidateTokenFailureException(exp)
|
|
241
254
|
except Exception as exp:
|
|
242
255
|
raise exp
|
|
243
256
|
|
|
244
257
|
def __validate_token(
|
|
245
|
-
self, token: str, options: Optional[
|
|
258
|
+
self, token: str, options: Optional[TokenValidationOptions] = None, audience: Optional[str] = None
|
|
246
259
|
) -> Dict[str, Any]:
|
|
247
260
|
"""
|
|
248
261
|
Method to validate token
|
|
249
262
|
|
|
250
263
|
:param token : token
|
|
251
264
|
:type : ``` str ```
|
|
265
|
+
:param options : validation options for issuer, audience, and scopes
|
|
266
|
+
:type : ``` TokenValidationOptions ```
|
|
267
|
+
:param audience : audience for validation
|
|
268
|
+
:type : ``` str ```
|
|
252
269
|
|
|
253
270
|
:returns:
|
|
254
271
|
payload
|
|
255
272
|
"""
|
|
273
|
+
# Convert TokenValidationOptions to jwt decode options if provided
|
|
274
|
+
jwt_options = {}
|
|
275
|
+
if options:
|
|
276
|
+
if options.issuer:
|
|
277
|
+
jwt_options["issuer"] = options.issuer
|
|
278
|
+
if options.audience:
|
|
279
|
+
jwt_options["audience"] = options.audience
|
|
280
|
+
jwt_options["verify_aud"] = True
|
|
281
|
+
elif audience is not None:
|
|
282
|
+
jwt_options["audience"] = [audience]
|
|
283
|
+
jwt_options["verify_aud"] = True
|
|
284
|
+
else:
|
|
285
|
+
jwt_options["verify_aud"] = False
|
|
286
|
+
|
|
256
287
|
self.core_client.get_jwks()
|
|
257
288
|
kid = jwt.get_unverified_header(token)["kid"]
|
|
258
289
|
key = self.core_client.keys[kid]
|
|
259
290
|
|
|
260
|
-
|
|
291
|
+
payload = jwt.decode(token, key=key, algorithms="RS256", options=jwt_options)
|
|
292
|
+
|
|
293
|
+
# Validate scopes if required
|
|
294
|
+
if options and options.required_scopes:
|
|
295
|
+
self.verify_scopes(token, options.required_scopes)
|
|
296
|
+
|
|
297
|
+
return payload
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def verify_scopes(self, token: str, required_scopes: list[str]) -> bool:
|
|
302
|
+
"""
|
|
303
|
+
Verify that the token contains the required scopes
|
|
304
|
+
|
|
305
|
+
:param token : The token to verify
|
|
306
|
+
:type : ``` str ```
|
|
307
|
+
:param required_scopes : The scopes that must be present in the token
|
|
308
|
+
:type : ``` list[str] ```
|
|
309
|
+
|
|
310
|
+
:returns:
|
|
311
|
+
bool: Returns True if all required scopes are present
|
|
312
|
+
:raises:
|
|
313
|
+
Error: If required scopes are missing, with details about which scopes are missing
|
|
314
|
+
"""
|
|
315
|
+
payload = jwt.decode(token, options={"verify_signature": False})
|
|
316
|
+
scopes = self.__extract_scopes_from_payload(payload)
|
|
317
|
+
|
|
318
|
+
missing_scopes = [scope for scope in required_scopes if scope not in scopes]
|
|
319
|
+
|
|
320
|
+
if missing_scopes:
|
|
321
|
+
raise ScalekitValidateTokenFailureException(f"Token missing required scopes: {', '.join(missing_scopes)}")
|
|
322
|
+
|
|
323
|
+
return True
|
|
324
|
+
|
|
325
|
+
def __extract_scopes_from_payload(self, payload: Dict[str, Any]) -> list[str]:
|
|
326
|
+
"""
|
|
327
|
+
Extract scopes from token payload
|
|
328
|
+
|
|
329
|
+
:param payload : The token payload
|
|
330
|
+
:type : ``` Dict[str, Any] ```
|
|
331
|
+
|
|
332
|
+
:returns:
|
|
333
|
+
list[str]: Array of scopes found in the token
|
|
334
|
+
"""
|
|
335
|
+
scopes = payload.get("scopes", [])
|
|
336
|
+
return [scope for scope in scopes if scope and scope.strip()]
|
|
261
337
|
|
|
262
338
|
def verify_webhook_payload(self, secret: str, headers: Dict[str, str], payload: [str | bytes]) -> bool:
|
|
263
339
|
"""
|
|
@@ -334,10 +410,10 @@ class ScalekitClient:
|
|
|
334
410
|
raise WebhookVerificationError("Invalid Signature Headers")
|
|
335
411
|
|
|
336
412
|
if timestamp < (now - webhook_tolerance_in_seconds):
|
|
337
|
-
raise
|
|
413
|
+
raise WebhookVerificationError("Message timestamp too old")
|
|
338
414
|
|
|
339
415
|
if timestamp > (now + webhook_tolerance_in_seconds):
|
|
340
|
-
raise
|
|
416
|
+
raise WebhookVerificationError("Message timestamp too new")
|
|
341
417
|
|
|
342
418
|
return timestamp
|
|
343
419
|
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
|
|
2
|
+
import grpc
|
|
3
|
+
from grpc import StatusCode
|
|
4
|
+
from http import HTTPStatus
|
|
5
|
+
from grpc_status import rpc_status
|
|
6
|
+
from requests.models import Response
|
|
7
|
+
from scalekit.v1.errdetails.errdetails_pb2 import ErrorInfo
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
GRPC_TO_HTTP = {
|
|
11
|
+
StatusCode.OK: HTTPStatus.OK,
|
|
12
|
+
StatusCode.INVALID_ARGUMENT: HTTPStatus.BAD_REQUEST,
|
|
13
|
+
StatusCode.FAILED_PRECONDITION: HTTPStatus.BAD_REQUEST,
|
|
14
|
+
StatusCode.OUT_OF_RANGE: HTTPStatus.BAD_REQUEST,
|
|
15
|
+
StatusCode.UNAUTHENTICATED: HTTPStatus.UNAUTHORIZED,
|
|
16
|
+
StatusCode.PERMISSION_DENIED: HTTPStatus.FORBIDDEN,
|
|
17
|
+
StatusCode.NOT_FOUND: HTTPStatus.NOT_FOUND,
|
|
18
|
+
StatusCode.ALREADY_EXISTS: HTTPStatus.CONFLICT,
|
|
19
|
+
StatusCode.ABORTED: HTTPStatus.CONFLICT,
|
|
20
|
+
StatusCode.RESOURCE_EXHAUSTED: HTTPStatus.TOO_MANY_REQUESTS,
|
|
21
|
+
StatusCode.CANCELLED: 499,
|
|
22
|
+
StatusCode.DATA_LOSS: HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
23
|
+
StatusCode.UNKNOWN: HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
24
|
+
StatusCode.INTERNAL: HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
25
|
+
StatusCode.UNIMPLEMENTED: HTTPStatus.NOT_IMPLEMENTED,
|
|
26
|
+
StatusCode.UNAVAILABLE: HTTPStatus.SERVICE_UNAVAILABLE,
|
|
27
|
+
StatusCode.DEADLINE_EXCEEDED: HTTPStatus.GATEWAY_TIMEOUT,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
HTTP_TO_GRPC = {
|
|
31
|
+
HTTPStatus.OK: StatusCode.OK,
|
|
32
|
+
HTTPStatus.BAD_REQUEST: StatusCode.INVALID_ARGUMENT,
|
|
33
|
+
HTTPStatus.UNAUTHORIZED: StatusCode.UNAUTHENTICATED,
|
|
34
|
+
HTTPStatus.FORBIDDEN: StatusCode.PERMISSION_DENIED,
|
|
35
|
+
HTTPStatus.NOT_FOUND: StatusCode.NOT_FOUND,
|
|
36
|
+
HTTPStatus.CONFLICT: StatusCode.ALREADY_EXISTS,
|
|
37
|
+
HTTPStatus.TOO_MANY_REQUESTS: StatusCode.RESOURCE_EXHAUSTED,
|
|
38
|
+
HTTPStatus.INTERNAL_SERVER_ERROR: StatusCode.INTERNAL,
|
|
39
|
+
HTTPStatus.NOT_IMPLEMENTED: StatusCode.UNIMPLEMENTED,
|
|
40
|
+
HTTPStatus.SERVICE_UNAVAILABLE: StatusCode.UNAVAILABLE,
|
|
41
|
+
HTTPStatus.GATEWAY_TIMEOUT: StatusCode.DEADLINE_EXCEEDED,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
HTTP_STATUS = {
|
|
46
|
+
'OK': HTTPStatus.OK,
|
|
47
|
+
'BAD_REQUEST': HTTPStatus.BAD_REQUEST,
|
|
48
|
+
'UNAUTHORIZED': HTTPStatus.UNAUTHORIZED,
|
|
49
|
+
'FORBIDDEN': HTTPStatus.FORBIDDEN,
|
|
50
|
+
'NOT_FOUND': HTTPStatus.NOT_FOUND,
|
|
51
|
+
'CONFLICT': HTTPStatus.CONFLICT,
|
|
52
|
+
'TOO_MANY_REQUESTS': HTTPStatus.TOO_MANY_REQUESTS,
|
|
53
|
+
'INTERNAL_SERVER_ERROR': HTTPStatus.INTERNAL_SERVER_ERROR,
|
|
54
|
+
'NOT_IMPLEMENTED': HTTPStatus.NOT_IMPLEMENTED,
|
|
55
|
+
'SERVICE_UNAVAILABLE': HTTPStatus.SERVICE_UNAVAILABLE,
|
|
56
|
+
'GATEWAY_TIMEOUT': HTTPStatus.GATEWAY_TIMEOUT,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class ScalekitException(Exception):
|
|
61
|
+
""" Base class for all scalekit exceptions """
|
|
62
|
+
def __init__(self, error):
|
|
63
|
+
super().__init__(error)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class WebhookVerificationError(ScalekitException):
|
|
67
|
+
""" Exception raised for webhook verification failure """
|
|
68
|
+
def __init__(self, error):
|
|
69
|
+
super().__init__(error)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class ScalekitValidateTokenFailureException(ScalekitException):
|
|
73
|
+
""" Exception raised for token validation failure """
|
|
74
|
+
def __init__(self, error):
|
|
75
|
+
super().__init__(error)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ScalekitServerException(ScalekitException):
|
|
79
|
+
""" Base class for all scalekit server exceptions """
|
|
80
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
81
|
+
super().__init__(error)
|
|
82
|
+
self._unpacked_details = list()
|
|
83
|
+
if isinstance(error, Response):
|
|
84
|
+
if error.reason and isinstance(error.reason, str):
|
|
85
|
+
self._http_status = HTTP_STATUS.get(error.reason.upper(), HTTPStatus.INTERNAL_SERVER_ERROR)
|
|
86
|
+
else:
|
|
87
|
+
self._http_status = HTTP_STATUS.get('INTERNAL_SERVER_ERROR')
|
|
88
|
+
self._grpc_status = HTTP_TO_GRPC.get(error.status_code, StatusCode.UNKNOWN)
|
|
89
|
+
self._error_code = error.reason
|
|
90
|
+
self._err_details = error.text
|
|
91
|
+
self._message = None
|
|
92
|
+
elif isinstance(error, grpc.RpcError):
|
|
93
|
+
self._grpc_status = error.code()
|
|
94
|
+
self._http_status = GRPC_TO_HTTP.get(self._grpc_status)
|
|
95
|
+
self._message = rpc_status.from_call(error).message
|
|
96
|
+
self._err_details = rpc_status.from_call(error).details
|
|
97
|
+
self._error_code = None
|
|
98
|
+
|
|
99
|
+
for detail in self._err_details:
|
|
100
|
+
info = ErrorInfo()
|
|
101
|
+
detail.Unpack(info)
|
|
102
|
+
self._unpacked_details.append(info)
|
|
103
|
+
if not self._error_code:
|
|
104
|
+
self._error_code = info.error_code
|
|
105
|
+
|
|
106
|
+
@staticmethod
|
|
107
|
+
def promote(error: Response | grpc.RpcError):
|
|
108
|
+
""" Promote a ScalekitServerException (Response or RpcError) to a specific error type """
|
|
109
|
+
grpc_status = HTTP_TO_GRPC.get(error.status_code) if isinstance(error, Response) else error.code()
|
|
110
|
+
|
|
111
|
+
if grpc_status == StatusCode.INVALID_ARGUMENT:
|
|
112
|
+
return ScalekitBadRequestException(error)
|
|
113
|
+
elif grpc_status == StatusCode.FAILED_PRECONDITION:
|
|
114
|
+
return ScalekitBadRequestException(error)
|
|
115
|
+
elif grpc_status == StatusCode.OUT_OF_RANGE:
|
|
116
|
+
return ScalekitBadRequestException(error)
|
|
117
|
+
elif grpc_status == StatusCode.UNAUTHENTICATED:
|
|
118
|
+
return ScalekitUnauthorizedException(error)
|
|
119
|
+
elif grpc_status == StatusCode.PERMISSION_DENIED:
|
|
120
|
+
return ScalekitForbiddenException(error)
|
|
121
|
+
elif grpc_status == StatusCode.NOT_FOUND:
|
|
122
|
+
return ScalekitNotFoundException(error)
|
|
123
|
+
elif grpc_status == StatusCode.ALREADY_EXISTS:
|
|
124
|
+
return ScalekitConflictException(error)
|
|
125
|
+
elif grpc_status == StatusCode.ABORTED:
|
|
126
|
+
return ScalekitConflictException(error)
|
|
127
|
+
elif grpc_status == StatusCode.RESOURCE_EXHAUSTED:
|
|
128
|
+
return ScalekitTooManyRequestsException(error)
|
|
129
|
+
elif grpc_status == StatusCode.CANCELLED:
|
|
130
|
+
return ScalekitCancelledException(error)
|
|
131
|
+
elif grpc_status == StatusCode.DATA_LOSS:
|
|
132
|
+
return ScalekitInternalServerException(error)
|
|
133
|
+
elif grpc_status == StatusCode.UNKNOWN:
|
|
134
|
+
return ScalekitInternalServerException(error)
|
|
135
|
+
elif grpc_status == StatusCode.INTERNAL:
|
|
136
|
+
return ScalekitInternalServerException(error)
|
|
137
|
+
elif grpc_status == StatusCode.UNIMPLEMENTED:
|
|
138
|
+
return ScalekitNotImplementedException(error)
|
|
139
|
+
elif grpc_status == StatusCode.UNAVAILABLE:
|
|
140
|
+
return ScalekitServiceUnavailableException(error)
|
|
141
|
+
elif grpc_status == StatusCode.DEADLINE_EXCEEDED:
|
|
142
|
+
return ScalekitGatewayTimeoutException(error)
|
|
143
|
+
else:
|
|
144
|
+
return ScalekitUnknownException(error)
|
|
145
|
+
|
|
146
|
+
def __str__(self):
|
|
147
|
+
if self._unpacked_details:
|
|
148
|
+
border = "=" * 40
|
|
149
|
+
details_str = str(self._unpacked_details)
|
|
150
|
+
if details_str.startswith("[") and "\n" in details_str:
|
|
151
|
+
details_str = details_str.replace("[", "[\n", 1)
|
|
152
|
+
return (f"\n{border}\n"
|
|
153
|
+
f"Error Code: {self._error_code}\n"
|
|
154
|
+
f"GRPC: ({self._grpc_status.name}: {self._grpc_status.value})\n"
|
|
155
|
+
f"HTTP: ({self._http_status.name}: {self._http_status.value})\n"
|
|
156
|
+
f"Error Details:\n"
|
|
157
|
+
f"{self._message}: {details_str}\n{border}\n")
|
|
158
|
+
else:
|
|
159
|
+
border = "=" * 40
|
|
160
|
+
return (f"\n{border}\n"
|
|
161
|
+
f"Error Code: {self._error_code}\n"
|
|
162
|
+
f"GRPC: ({self._grpc_status.name}: {self._grpc_status.value})\n"
|
|
163
|
+
f"HTTP: ({self._http_status.name}: {self._http_status.value})\n"
|
|
164
|
+
f"Error Details: {self._err_details}\n{border}\n")
|
|
165
|
+
|
|
166
|
+
@property
|
|
167
|
+
def http_status(self):
|
|
168
|
+
""" Getter for HTTP status code """
|
|
169
|
+
return self._http_status
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def error_code(self):
|
|
173
|
+
""" Getter for Error code """
|
|
174
|
+
return self._error_code
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def err_details(self):
|
|
178
|
+
""" Getter for Error details object """
|
|
179
|
+
return self._err_details
|
|
180
|
+
|
|
181
|
+
@property
|
|
182
|
+
def grpc_status(self):
|
|
183
|
+
""" Getter for GRPC status code """
|
|
184
|
+
return self._grpc_status
|
|
185
|
+
|
|
186
|
+
@property
|
|
187
|
+
def message(self):
|
|
188
|
+
""" Getter for Exception message """
|
|
189
|
+
return self._message
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
class ScalekitBadRequestException(ScalekitServerException):
|
|
193
|
+
""" Scalekit Exception raised for bad requests """
|
|
194
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
195
|
+
super().__init__(error)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ScalekitUnauthorizedException(ScalekitServerException):
|
|
199
|
+
""" Scalekit Exception raised for unauthorized access """
|
|
200
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
201
|
+
super().__init__(error)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
class ScalekitForbiddenException(ScalekitServerException):
|
|
205
|
+
""" Scalekit Exception raised for forbidden access """
|
|
206
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
207
|
+
super().__init__(error)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
class ScalekitNotFoundException(ScalekitServerException):
|
|
211
|
+
""" Scalekit Exception raised when a resource is not found """
|
|
212
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
213
|
+
super().__init__(error)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class ScalekitConflictException(ScalekitServerException):
|
|
217
|
+
""" Scalekit Exception raised for conflicts, such as duplicate resources """
|
|
218
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
219
|
+
super().__init__(error)
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class ScalekitTooManyRequestsException(ScalekitServerException):
|
|
223
|
+
""" Scalekit Exception raised when too many requests are made in a short time """
|
|
224
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
225
|
+
super().__init__(error)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class ScalekitInternalServerException(ScalekitServerException):
|
|
229
|
+
""" Scalekit Exception raised for internal server errors """
|
|
230
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
231
|
+
super().__init__(error)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class ScalekitNotImplementedException(ScalekitServerException):
|
|
235
|
+
""" Scalekit Exception raised when a feature is not implemented """
|
|
236
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
237
|
+
super().__init__(error)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class ScalekitServiceUnavailableException(ScalekitServerException):
|
|
241
|
+
""" Scalekit Exception raised when the service is unavailable """
|
|
242
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
243
|
+
super().__init__(error)
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
class ScalekitGatewayTimeoutException(ScalekitServerException):
|
|
247
|
+
""" Scalekit Exception raised when a gateway timeout occurs """
|
|
248
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
249
|
+
super().__init__(error)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class ScalekitCancelledException(ScalekitServerException):
|
|
253
|
+
""" Scalekit Exception raised when an operation is cancelled """
|
|
254
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
255
|
+
super().__init__(error)
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
class ScalekitUnknownException(ScalekitServerException):
|
|
259
|
+
def __init__(self, error: Response | grpc.RpcError):
|
|
260
|
+
super().__init__(error)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from enum import Enum
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, List
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class GrantType(Enum):
|
|
@@ -71,4 +71,17 @@ class LogoutUrlOptions:
|
|
|
71
71
|
):
|
|
72
72
|
self.id_token_hint = id_token_hint
|
|
73
73
|
self.post_logout_redirect_uri = post_logout_redirect_uri
|
|
74
|
-
self.state = state
|
|
74
|
+
self.state = state
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class TokenValidationOptions:
|
|
78
|
+
"""Options for token validation including issuer, audience, and scope validation"""
|
|
79
|
+
def __init__(
|
|
80
|
+
self,
|
|
81
|
+
issuer: Optional[str] = None,
|
|
82
|
+
audience: Optional[List[str]] = None,
|
|
83
|
+
required_scopes: Optional[List[str]] = None
|
|
84
|
+
):
|
|
85
|
+
self.issuer = issuer
|
|
86
|
+
self.audience = audience
|
|
87
|
+
self.required_scopes = required_scopes
|
|
@@ -8,9 +8,8 @@ import platform
|
|
|
8
8
|
from urllib.parse import urlparse
|
|
9
9
|
|
|
10
10
|
from cryptography.hazmat.primitives import serialization
|
|
11
|
-
from grpc_status import rpc_status
|
|
12
11
|
from scalekit.common.scalekit import GrantType
|
|
13
|
-
from scalekit.
|
|
12
|
+
from scalekit.common.exceptions import ScalekitServerException, ScalekitException
|
|
14
13
|
|
|
15
14
|
TRequest = TypeVar("TRequest")
|
|
16
15
|
TResponse = TypeVar("TResponse")
|
|
@@ -27,7 +26,7 @@ class WithCall(Protocol):
|
|
|
27
26
|
class CoreClient:
|
|
28
27
|
"""Class definition for Core Client"""
|
|
29
28
|
|
|
30
|
-
sdk_version = "Scalekit-Python/2.
|
|
29
|
+
sdk_version = "Scalekit-Python/2.3.0"
|
|
31
30
|
api_version = "20250718"
|
|
32
31
|
user_agent = f"{sdk_version} Python/{platform.python_version()} ({platform.system()}; {platform.architecture()}"
|
|
33
32
|
|
|
@@ -86,7 +85,7 @@ class CoreClient:
|
|
|
86
85
|
|
|
87
86
|
response = self.authenticate(data=json.dumps(params))
|
|
88
87
|
if response.status_code != 200:
|
|
89
|
-
raise
|
|
88
|
+
raise ScalekitServerException.promote(response)
|
|
90
89
|
response = json.loads(response.content)
|
|
91
90
|
self.access_token = response["access_token"]
|
|
92
91
|
|
|
@@ -105,7 +104,7 @@ class CoreClient:
|
|
|
105
104
|
verify=True,
|
|
106
105
|
)
|
|
107
106
|
if response.status_code != 200:
|
|
108
|
-
raise
|
|
107
|
+
raise ScalekitServerException.promote(response)
|
|
109
108
|
return response
|
|
110
109
|
|
|
111
110
|
def get_jwks(self):
|
|
@@ -129,38 +128,6 @@ class CoreClient:
|
|
|
129
128
|
|
|
130
129
|
self.keys[kid] = pem_key.decode("utf-8")
|
|
131
130
|
|
|
132
|
-
def grpc_exec(
|
|
133
|
-
self,
|
|
134
|
-
func: WithCall,
|
|
135
|
-
data: TRequest,
|
|
136
|
-
retry=1,
|
|
137
|
-
) -> TResponse:
|
|
138
|
-
try:
|
|
139
|
-
resp = func(
|
|
140
|
-
data,
|
|
141
|
-
metadata=tuple(self.get_headers().items()),
|
|
142
|
-
)
|
|
143
|
-
return resp
|
|
144
|
-
except grpc.RpcError as exp:
|
|
145
|
-
if retry > 0:
|
|
146
|
-
return self.grpc_exec(func, data, retry=retry - 1)
|
|
147
|
-
else:
|
|
148
|
-
status_code = exp.code()
|
|
149
|
-
status = rpc_status.from_call(exp)
|
|
150
|
-
messages = [status.message]
|
|
151
|
-
if status_code == grpc.StatusCode.INVALID_ARGUMENT:
|
|
152
|
-
for detail in status.details:
|
|
153
|
-
if detail.Is(ErrorInfo.DESCRIPTOR):
|
|
154
|
-
info = ErrorInfo()
|
|
155
|
-
detail.Unpack(info)
|
|
156
|
-
if info.validation_error_info:
|
|
157
|
-
for fv in info.validation_error_info.field_violations:
|
|
158
|
-
messages.append(f"{fv.field}: {fv.description}")
|
|
159
|
-
|
|
160
|
-
raise Exception("\n".join(messages))
|
|
161
|
-
except Exception as exp:
|
|
162
|
-
raise exp
|
|
163
|
-
|
|
164
131
|
def get_headers(self, headers: Optional[dict] = None) -> dict:
|
|
165
132
|
"""
|
|
166
133
|
Method to get user defined headers and returns collated header params
|
|
@@ -180,3 +147,23 @@ class CoreClient:
|
|
|
180
147
|
if headers:
|
|
181
148
|
return {**default_headers, **headers}
|
|
182
149
|
return default_headers
|
|
150
|
+
|
|
151
|
+
def grpc_exec(
|
|
152
|
+
self,
|
|
153
|
+
func: WithCall,
|
|
154
|
+
data: TRequest,
|
|
155
|
+
retry=1,
|
|
156
|
+
) -> TResponse:
|
|
157
|
+
try:
|
|
158
|
+
resp = func(
|
|
159
|
+
data,
|
|
160
|
+
metadata=tuple(self.get_headers().items()),
|
|
161
|
+
)
|
|
162
|
+
return resp
|
|
163
|
+
except grpc.RpcError as exp:
|
|
164
|
+
if retry > 0:
|
|
165
|
+
return self.grpc_exec(func, data, retry=retry - 1)
|
|
166
|
+
else:
|
|
167
|
+
raise ScalekitServerException.promote(exp)
|
|
168
|
+
except Exception as exp:
|
|
169
|
+
raise ScalekitException(exp)
|