skyflow-flowvault-python 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- common/.coveragerc +4 -0
- common/__init__.py +3 -0
- common/client/__init__.py +0 -0
- common/client/base_skyflow.py +345 -0
- common/client/utils/__init__.py +1 -0
- common/client/utils/_utils.py +127 -0
- common/errors/__init__.py +1 -0
- common/errors/_skyflow_error.py +17 -0
- common/generated/__init__.py +0 -0
- common/generated/rest/__init__.py +24 -0
- common/generated/rest/authentication/__init__.py +4 -0
- common/generated/rest/authentication/client.py +181 -0
- common/generated/rest/authentication/raw_client.py +241 -0
- common/generated/rest/client.py +153 -0
- common/generated/rest/core/__init__.py +52 -0
- common/generated/rest/core/api_error.py +23 -0
- common/generated/rest/core/client_wrapper.py +86 -0
- common/generated/rest/core/datetime_utils.py +28 -0
- common/generated/rest/core/file.py +67 -0
- common/generated/rest/core/force_multipart.py +16 -0
- common/generated/rest/core/http_client.py +543 -0
- common/generated/rest/core/http_response.py +55 -0
- common/generated/rest/core/jsonable_encoder.py +100 -0
- common/generated/rest/core/pydantic_utilities.py +255 -0
- common/generated/rest/core/query_encoder.py +58 -0
- common/generated/rest/core/remove_none_from_dict.py +11 -0
- common/generated/rest/core/request_options.py +35 -0
- common/generated/rest/core/serialization.py +276 -0
- common/generated/rest/environment.py +8 -0
- common/generated/rest/errors/__init__.py +9 -0
- common/generated/rest/errors/bad_request_error.py +14 -0
- common/generated/rest/errors/not_found_error.py +14 -0
- common/generated/rest/errors/unauthorized_error.py +14 -0
- common/generated/rest/py.typed +0 -0
- common/generated/rest/types/__init__.py +9 -0
- common/generated/rest/types/googlerpc_status.py +22 -0
- common/generated/rest/types/protobuf_any.py +21 -0
- common/generated/rest/types/v_1_get_auth_token_response.py +33 -0
- common/generated/rest/version.py +6 -0
- common/service_account/__init__.py +1 -0
- common/service_account/_utils.py +248 -0
- common/service_account/client/__init__.py +0 -0
- common/service_account/client/auth_client.py +13 -0
- common/utils/__init__.py +4 -0
- common/utils/_helpers.py +18 -0
- common/utils/_skyflow_messages.py +445 -0
- common/utils/_utils.py +50 -0
- common/utils/constants.py +291 -0
- common/utils/enums/__init__.py +12 -0
- common/utils/enums/content_types.py +9 -0
- common/utils/enums/detect_entities.py +73 -0
- common/utils/enums/detect_output_transcriptions.py +8 -0
- common/utils/enums/env.py +13 -0
- common/utils/enums/log_level.py +8 -0
- common/utils/enums/masking_method.py +5 -0
- common/utils/enums/redaction_type.py +7 -0
- common/utils/enums/request_method.py +8 -0
- common/utils/enums/token_mode.py +6 -0
- common/utils/enums/token_type.py +6 -0
- common/utils/logger/__init__.py +2 -0
- common/utils/logger/_log_helpers.py +47 -0
- common/utils/logger/_logger.py +50 -0
- common/utils/validations/__init__.py +11 -0
- common/utils/validations/_validations.py +267 -0
- common/vault/base_vault_client.py +122 -0
- common/vault/base_vault_controller.py +58 -0
- common/vault/data/__init__.py +2 -0
- common/vault/data/_base_insert_request.py +7 -0
- common/vault/data/_base_insert_response.py +11 -0
- skyflow/__init__.py +2 -0
- skyflow/client/__init__.py +1 -0
- skyflow/client/_http_config_builder.py +45 -0
- skyflow/client/skyflow.py +15 -0
- skyflow/error/__init__.py +3 -0
- skyflow/generated/__init__.py +0 -0
- skyflow/generated/rest/__init__.py +164 -0
- skyflow/generated/rest/_default_clients.py +32 -0
- skyflow/generated/rest/client.py +385 -0
- skyflow/generated/rest/core/__init__.py +127 -0
- skyflow/generated/rest/core/api_error.py +23 -0
- skyflow/generated/rest/core/client_wrapper.py +148 -0
- skyflow/generated/rest/core/datetime_utils.py +70 -0
- skyflow/generated/rest/core/file.py +67 -0
- skyflow/generated/rest/core/force_multipart.py +18 -0
- skyflow/generated/rest/core/http_client.py +940 -0
- skyflow/generated/rest/core/http_response.py +63 -0
- skyflow/generated/rest/core/http_sse/__init__.py +42 -0
- skyflow/generated/rest/core/http_sse/_api.py +455 -0
- skyflow/generated/rest/core/http_sse/_decoders.py +74 -0
- skyflow/generated/rest/core/http_sse/_exceptions.py +7 -0
- skyflow/generated/rest/core/http_sse/_models.py +17 -0
- skyflow/generated/rest/core/jsonable_encoder.py +133 -0
- skyflow/generated/rest/core/logging.py +107 -0
- skyflow/generated/rest/core/parse_error.py +36 -0
- skyflow/generated/rest/core/pydantic_utilities.py +486 -0
- skyflow/generated/rest/core/query_encoder.py +58 -0
- skyflow/generated/rest/core/remove_none_from_dict.py +11 -0
- skyflow/generated/rest/core/request_options.py +40 -0
- skyflow/generated/rest/core/serialization.py +347 -0
- skyflow/generated/rest/environment.py +8 -0
- skyflow/generated/rest/errors/__init__.py +53 -0
- skyflow/generated/rest/errors/bad_request_error.py +11 -0
- skyflow/generated/rest/errors/forbidden_error.py +11 -0
- skyflow/generated/rest/errors/internal_server_error.py +11 -0
- skyflow/generated/rest/errors/not_found_error.py +11 -0
- skyflow/generated/rest/errors/too_many_requests_error.py +11 -0
- skyflow/generated/rest/errors/unauthorized_error.py +11 -0
- skyflow/generated/rest/py.typed +0 -0
- skyflow/generated/rest/query/__init__.py +4 -0
- skyflow/generated/rest/query/client.py +139 -0
- skyflow/generated/rest/query/raw_client.py +239 -0
- skyflow/generated/rest/raw_client.py +86 -0
- skyflow/generated/rest/records/__init__.py +34 -0
- skyflow/generated/rest/records/client.py +685 -0
- skyflow/generated/rest/records/raw_client.py +988 -0
- skyflow/generated/rest/records/types/__init__.py +34 -0
- skyflow/generated/rest/records/types/update_request_update_type.py +5 -0
- skyflow/generated/rest/tokens/__init__.py +4 -0
- skyflow/generated/rest/tokens/client.py +246 -0
- skyflow/generated/rest/tokens/raw_client.py +507 -0
- skyflow/generated/rest/types/__init__.py +116 -0
- skyflow/generated/rest/types/column_redactions.py +33 -0
- skyflow/generated/rest/types/delete_response.py +23 -0
- skyflow/generated/rest/types/delete_response_object.py +42 -0
- skyflow/generated/rest/types/detokenize_response.py +23 -0
- skyflow/generated/rest/types/detokenize_response_object.py +54 -0
- skyflow/generated/rest/types/error_response.py +20 -0
- skyflow/generated/rest/types/error_response_error.py +28 -0
- skyflow/generated/rest/types/execute_query_record_response.py +22 -0
- skyflow/generated/rest/types/execute_query_response.py +26 -0
- skyflow/generated/rest/types/execute_query_response_metadata.py +26 -0
- skyflow/generated/rest/types/get_request_data.py +60 -0
- skyflow/generated/rest/types/get_response.py +23 -0
- skyflow/generated/rest/types/get_tokens_from_values_request_object.py +30 -0
- skyflow/generated/rest/types/get_tokens_from_values_response.py +23 -0
- skyflow/generated/rest/types/google_protobuf_value.py +8 -0
- skyflow/generated/rest/types/http_code.py +6 -0
- skyflow/generated/rest/types/insert_record_data.py +36 -0
- skyflow/generated/rest/types/insert_response.py +23 -0
- skyflow/generated/rest/types/record_response_object.py +70 -0
- skyflow/generated/rest/types/token_group_redactions.py +33 -0
- skyflow/generated/rest/types/tokenize_response_object.py +49 -0
- skyflow/generated/rest/types/unique_value.py +22 -0
- skyflow/generated/rest/types/update_record_data.py +52 -0
- skyflow/generated/rest/types/update_record_data_update_type.py +5 -0
- skyflow/generated/rest/types/update_response.py +23 -0
- skyflow/generated/rest/types/upsert.py +45 -0
- skyflow/generated/rest/types/upsert_update_type.py +5 -0
- skyflow/generated/rest/version.py +6 -0
- skyflow/service_account/__init__.py +15 -0
- skyflow/utils/__init__.py +9 -0
- skyflow/utils/_http_config.py +36 -0
- skyflow/utils/_response_parsing.py +66 -0
- skyflow/utils/_retry.py +97 -0
- skyflow/utils/_skyflow_messages.py +114 -0
- skyflow/utils/_utils.py +54 -0
- skyflow/utils/_version.py +1 -0
- skyflow/utils/enums/__init__.py +3 -0
- skyflow/utils/enums/_custom_header_key.py +10 -0
- skyflow/utils/enums/_env_urls.py +9 -0
- skyflow/utils/enums/_upsert_type.py +7 -0
- skyflow/utils/validations/__init__.py +9 -0
- skyflow/utils/validations/_validations.py +195 -0
- skyflow/vault/__init__.py +0 -0
- skyflow/vault/client/__init__.py +0 -0
- skyflow/vault/client/client.py +102 -0
- skyflow/vault/controller/__init__.py +1 -0
- skyflow/vault/controller/_vault.py +475 -0
- skyflow/vault/data/__init__.py +30 -0
- skyflow/vault/data/_column_redactions.py +4 -0
- skyflow/vault/data/_delete_options.py +8 -0
- skyflow/vault/data/_delete_request.py +5 -0
- skyflow/vault/data/_delete_response.py +9 -0
- skyflow/vault/data/_delete_response_record.py +9 -0
- skyflow/vault/data/_detokenize_options.py +8 -0
- skyflow/vault/data/_detokenize_request.py +9 -0
- skyflow/vault/data/_detokenize_response.py +9 -0
- skyflow/vault/data/_detokenize_response_record.py +16 -0
- skyflow/vault/data/_detokenize_response_record_metadata.py +10 -0
- skyflow/vault/data/_get_options.py +8 -0
- skyflow/vault/data/_get_request.py +17 -0
- skyflow/vault/data/_get_request_record.py +13 -0
- skyflow/vault/data/_get_response.py +9 -0
- skyflow/vault/data/_get_response_record.py +17 -0
- skyflow/vault/data/_insert_options.py +8 -0
- skyflow/vault/data/_insert_request.py +11 -0
- skyflow/vault/data/_insert_request_record.py +9 -0
- skyflow/vault/data/_insert_response.py +9 -0
- skyflow/vault/data/_insert_response_record.py +17 -0
- skyflow/vault/data/_request_context.py +16 -0
- skyflow/vault/data/_token.py +8 -0
- skyflow/vault/data/_token_group_redactions.py +4 -0
- skyflow/vault/data/_update_options.py +8 -0
- skyflow/vault/data/_update_request.py +11 -0
- skyflow/vault/data/_update_request_record.py +6 -0
- skyflow/vault/data/_update_response.py +9 -0
- skyflow/vault/data/_update_response_record.py +17 -0
- skyflow/vault/data/_upsert_options.py +7 -0
- skyflow_flowvault_python-1.0.0.dist-info/METADATA +896 -0
- skyflow_flowvault_python-1.0.0.dist-info/RECORD +202 -0
- skyflow_flowvault_python-1.0.0.dist-info/WHEEL +5 -0
- skyflow_flowvault_python-1.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
from ..core.api_error import ApiError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class UnauthorizedError(ApiError):
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
body: typing.Dict[str, typing.Optional[typing.Any]],
|
|
12
|
+
headers: typing.Optional[typing.Dict[str, str]] = None,
|
|
13
|
+
):
|
|
14
|
+
super().__init__(status_code=401, headers=headers, body=body)
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
# isort: skip_file
|
|
4
|
+
|
|
5
|
+
from .googlerpc_status import GooglerpcStatus
|
|
6
|
+
from .protobuf_any import ProtobufAny
|
|
7
|
+
from .v_1_get_auth_token_response import V1GetAuthTokenResponse
|
|
8
|
+
|
|
9
|
+
__all__ = ["GooglerpcStatus", "ProtobufAny", "V1GetAuthTokenResponse"]
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
6
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
|
|
7
|
+
from .protobuf_any import ProtobufAny
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class GooglerpcStatus(UniversalBaseModel):
|
|
11
|
+
code: typing.Optional[int] = None
|
|
12
|
+
message: typing.Optional[str] = None
|
|
13
|
+
details: typing.Optional[typing.List[ProtobufAny]] = None
|
|
14
|
+
|
|
15
|
+
if IS_PYDANTIC_V2:
|
|
16
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
|
17
|
+
else:
|
|
18
|
+
|
|
19
|
+
class Config:
|
|
20
|
+
frozen = True
|
|
21
|
+
smart_union = True
|
|
22
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
6
|
+
import typing_extensions
|
|
7
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
|
|
8
|
+
from ..core.serialization import FieldMetadata
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ProtobufAny(UniversalBaseModel):
|
|
12
|
+
type: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="@type")] = None
|
|
13
|
+
|
|
14
|
+
if IS_PYDANTIC_V2:
|
|
15
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
|
16
|
+
else:
|
|
17
|
+
|
|
18
|
+
class Config:
|
|
19
|
+
frozen = True
|
|
20
|
+
smart_union = True
|
|
21
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# This file was auto-generated by Fern from our API Definition.
|
|
2
|
+
|
|
3
|
+
import typing
|
|
4
|
+
|
|
5
|
+
import pydantic
|
|
6
|
+
import typing_extensions
|
|
7
|
+
from ..core.pydantic_utilities import IS_PYDANTIC_V2, UniversalBaseModel
|
|
8
|
+
from ..core.serialization import FieldMetadata
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class V1GetAuthTokenResponse(UniversalBaseModel):
|
|
12
|
+
access_token: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="accessToken")] = (
|
|
13
|
+
pydantic.Field(default=None)
|
|
14
|
+
)
|
|
15
|
+
"""
|
|
16
|
+
AccessToken.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
token_type: typing_extensions.Annotated[typing.Optional[str], FieldMetadata(alias="tokenType")] = pydantic.Field(
|
|
20
|
+
default=None
|
|
21
|
+
)
|
|
22
|
+
"""
|
|
23
|
+
TokenType : Bearer.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
if IS_PYDANTIC_V2:
|
|
27
|
+
model_config: typing.ClassVar[pydantic.ConfigDict] = pydantic.ConfigDict(extra="allow", frozen=True) # type: ignore # Pydantic v2
|
|
28
|
+
else:
|
|
29
|
+
|
|
30
|
+
class Config:
|
|
31
|
+
frozen = True
|
|
32
|
+
smart_union = True
|
|
33
|
+
extra = pydantic.Extra.allow
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
# NOTE: hand-patched, not Fern-generated content. Fern originally emitted a runtime
|
|
2
|
+
# metadata.version("skyflow.generated.rest") lookup here, but this code is bundled into the
|
|
3
|
+
# skyflow (v2) and v3 wheels via a build_py hook rather than published under that distribution
|
|
4
|
+
# name, so the lookup always raised PackageNotFoundError on import. Hardcoded until the Fern
|
|
5
|
+
# generator config (skyflow-fern-config) is updated to stop emitting a runtime lookup here.
|
|
6
|
+
__version__ = "0.0.9"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from ._utils import generate_bearer_token, generate_bearer_token_from_creds, is_expired, generate_signed_data_tokens, generate_signed_data_tokens_from_creds
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import datetime
|
|
3
|
+
import re
|
|
4
|
+
import time
|
|
5
|
+
import jwt
|
|
6
|
+
from urllib.parse import urlparse
|
|
7
|
+
from common.errors import SkyflowError
|
|
8
|
+
from common.service_account.client.auth_client import AuthClient
|
|
9
|
+
from common.utils.logger import log_info, log_error_log
|
|
10
|
+
from common.utils import get_base_url, format_scope, SkyflowMessages
|
|
11
|
+
from common.utils.constants import JWT, CredentialField, JwtField, OptionField, ResponseField
|
|
12
|
+
from common.generated.rest.errors.unauthorized_error import UnauthorizedError
|
|
13
|
+
from common.utils import is_valid_url
|
|
14
|
+
from common.utils.constants import CTX_KEY_REGEX
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
invalid_input_error_code = SkyflowMessages.ErrorCodes.INVALID_INPUT.value
|
|
18
|
+
|
|
19
|
+
_CTX_KEY_PATTERN = re.compile(CTX_KEY_REGEX)
|
|
20
|
+
|
|
21
|
+
_SNAKE_TO_CAMEL_CRED_MAP = {
|
|
22
|
+
'private_key': CredentialField.PRIVATE_KEY,
|
|
23
|
+
'client_id': CredentialField.CLIENT_ID,
|
|
24
|
+
'key_id': CredentialField.KEY_ID,
|
|
25
|
+
'token_uri': CredentialField.TOKEN_URI,
|
|
26
|
+
'client_name': CredentialField.CLIENT_NAME,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _normalize_credentials(credentials):
|
|
31
|
+
return {_SNAKE_TO_CAMEL_CRED_MAP.get(k, k): v for k, v in credentials.items()}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _validate_and_resolve_ctx(ctx, messages=None):
|
|
35
|
+
"""Validate ctx value and return resolved value for JWT claims.
|
|
36
|
+
Returns None if ctx should be omitted, the value if valid, or raises SkyflowError if invalid.
|
|
37
|
+
"""
|
|
38
|
+
messages = messages or SkyflowMessages
|
|
39
|
+
if ctx is None:
|
|
40
|
+
return None
|
|
41
|
+
if isinstance(ctx, str):
|
|
42
|
+
if ctx.strip() == '':
|
|
43
|
+
return None
|
|
44
|
+
return ctx
|
|
45
|
+
if isinstance(ctx, dict):
|
|
46
|
+
if len(ctx) == 0:
|
|
47
|
+
return None
|
|
48
|
+
for key in ctx:
|
|
49
|
+
if not isinstance(key, str) or not _CTX_KEY_PATTERN.match(key):
|
|
50
|
+
raise SkyflowError(
|
|
51
|
+
messages.Error.INVALID_CTX_MAP_KEY.value.format(key),
|
|
52
|
+
invalid_input_error_code
|
|
53
|
+
)
|
|
54
|
+
return ctx
|
|
55
|
+
if isinstance(ctx, (bool, int, float)):
|
|
56
|
+
return ctx
|
|
57
|
+
raise SkyflowError(
|
|
58
|
+
messages.Error.INVALID_CTX_TYPE.value,
|
|
59
|
+
invalid_input_error_code
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def is_expired(token, logger = None):
|
|
63
|
+
if token is None:
|
|
64
|
+
return True
|
|
65
|
+
if len(token) == 0:
|
|
66
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_BEARER_TOKEN.value)
|
|
67
|
+
return True
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
decoded = jwt.decode(
|
|
71
|
+
token, options={OptionField.VERIFY_SIGNATURE: False, OptionField.VERIFY_AUD: False})
|
|
72
|
+
if time.time() >= decoded[JwtField.EXP]:
|
|
73
|
+
log_info(SkyflowMessages.Info.BEARER_TOKEN_EXPIRED.value, logger)
|
|
74
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_BEARER_TOKEN.value)
|
|
75
|
+
return True
|
|
76
|
+
return False
|
|
77
|
+
except jwt.ExpiredSignatureError:
|
|
78
|
+
return True
|
|
79
|
+
except Exception:
|
|
80
|
+
log_error_log(SkyflowMessages.Error.JWT_DECODE_ERROR.value, logger)
|
|
81
|
+
return True
|
|
82
|
+
|
|
83
|
+
def generate_bearer_token(credentials_file_path, options = None, logger = None):
|
|
84
|
+
log_info(SkyflowMessages.Info.GET_BEARER_TOKEN_TRIGGERED.value, logger)
|
|
85
|
+
try:
|
|
86
|
+
with open(credentials_file_path, 'r') as credentials_file:
|
|
87
|
+
try:
|
|
88
|
+
credentials = json.load(credentials_file)
|
|
89
|
+
except Exception:
|
|
90
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_CREDENTIALS_FILE.value, logger=logger)
|
|
91
|
+
raise SkyflowError(SkyflowMessages.Error.FILE_INVALID_JSON.value.format(credentials_file_path), invalid_input_error_code)
|
|
92
|
+
except SkyflowError:
|
|
93
|
+
raise
|
|
94
|
+
except Exception:
|
|
95
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIAL_FILE_PATH.value, invalid_input_error_code)
|
|
96
|
+
result = get_service_account_token(credentials, options, logger)
|
|
97
|
+
return result
|
|
98
|
+
|
|
99
|
+
def generate_bearer_token_from_creds(credentials, options = None, logger = None):
|
|
100
|
+
log_info(SkyflowMessages.Info.GET_BEARER_TOKEN_TRIGGERED.value, logger)
|
|
101
|
+
credentials = credentials.strip()
|
|
102
|
+
try:
|
|
103
|
+
json_credentials = json.loads(credentials.replace('\n', '\\n'))
|
|
104
|
+
except Exception:
|
|
105
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIALS_STRING.value, invalid_input_error_code)
|
|
106
|
+
result = get_service_account_token(json_credentials, options, logger)
|
|
107
|
+
return result
|
|
108
|
+
|
|
109
|
+
def get_service_account_token(credentials, options, logger):
|
|
110
|
+
credentials = _normalize_credentials(credentials)
|
|
111
|
+
try:
|
|
112
|
+
private_key = credentials[CredentialField.PRIVATE_KEY]
|
|
113
|
+
except KeyError:
|
|
114
|
+
log_error_log(SkyflowMessages.ErrorLogs.PRIVATE_KEY_IS_REQUIRED.value, logger=logger)
|
|
115
|
+
raise SkyflowError(SkyflowMessages.Error.MISSING_PRIVATE_KEY.value, invalid_input_error_code)
|
|
116
|
+
try:
|
|
117
|
+
client_id = credentials[CredentialField.CLIENT_ID]
|
|
118
|
+
except KeyError:
|
|
119
|
+
log_error_log(SkyflowMessages.ErrorLogs.CLIENT_ID_IS_REQUIRED.value, logger=logger)
|
|
120
|
+
raise SkyflowError(SkyflowMessages.Error.MISSING_CLIENT_ID.value, invalid_input_error_code)
|
|
121
|
+
try:
|
|
122
|
+
key_id = credentials[CredentialField.KEY_ID]
|
|
123
|
+
except KeyError:
|
|
124
|
+
log_error_log(SkyflowMessages.ErrorLogs.KEY_ID_IS_REQUIRED.value, logger=logger)
|
|
125
|
+
raise SkyflowError(SkyflowMessages.Error.MISSING_KEY_ID.value, invalid_input_error_code)
|
|
126
|
+
try:
|
|
127
|
+
token_uri = credentials[CredentialField.TOKEN_URI]
|
|
128
|
+
except KeyError:
|
|
129
|
+
log_error_log(SkyflowMessages.ErrorLogs.TOKEN_URI_IS_REQUIRED.value, logger=logger)
|
|
130
|
+
raise SkyflowError(SkyflowMessages.Error.MISSING_TOKEN_URI.value, invalid_input_error_code)
|
|
131
|
+
|
|
132
|
+
if not isinstance(token_uri, str) or not is_valid_url(token_uri):
|
|
133
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_TOKEN_URI.value, logger=logger)
|
|
134
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_TOKEN_URI.value, invalid_input_error_code)
|
|
135
|
+
|
|
136
|
+
if options and CredentialField.TOKEN_URI_OPTION in options:
|
|
137
|
+
token_uri = options[CredentialField.TOKEN_URI_OPTION]
|
|
138
|
+
if not isinstance(token_uri, str) or not is_valid_url(token_uri):
|
|
139
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_TOKEN_URI.value, logger=logger)
|
|
140
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_TOKEN_URI.value, invalid_input_error_code)
|
|
141
|
+
|
|
142
|
+
signed_token = get_signed_jwt(options, client_id, key_id, token_uri, private_key, logger)
|
|
143
|
+
base_url = get_base_url(token_uri)
|
|
144
|
+
auth_client = AuthClient(base_url)
|
|
145
|
+
auth_api = auth_client.get_auth_api()
|
|
146
|
+
|
|
147
|
+
formatted_scope = None
|
|
148
|
+
if options and OptionField.ROLE_IDS in options:
|
|
149
|
+
formatted_scope = format_scope(options.get(OptionField.ROLE_IDS))
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
response = auth_api.authentication_service_get_auth_token(assertion = signed_token,
|
|
153
|
+
grant_type=JWT.GRANT_TYPE_JWT_BEARER,
|
|
154
|
+
scope=formatted_scope)
|
|
155
|
+
log_info(SkyflowMessages.Info.GET_BEARER_TOKEN_SUCCESS.value, logger)
|
|
156
|
+
except UnauthorizedError:
|
|
157
|
+
log_error_log(SkyflowMessages.ErrorLogs.UNAUTHORIZED_ERROR_IN_GETTING_BEARER_TOKEN.value, logger=logger)
|
|
158
|
+
raise SkyflowError(SkyflowMessages.Error.UNAUTHORIZED_ERROR_IN_GETTING_BEARER_TOKEN.value, invalid_input_error_code)
|
|
159
|
+
except Exception:
|
|
160
|
+
log_error_log(SkyflowMessages.ErrorLogs.FAILED_TO_GET_BEARER_TOKEN.value, logger=logger)
|
|
161
|
+
raise SkyflowError(SkyflowMessages.Error.FAILED_TO_GET_BEARER_TOKEN.value, invalid_input_error_code)
|
|
162
|
+
return response.access_token, response.token_type
|
|
163
|
+
|
|
164
|
+
def get_signed_jwt(options, client_id, key_id, token_uri, private_key, logger):
|
|
165
|
+
payload = {
|
|
166
|
+
JwtField.ISS: client_id,
|
|
167
|
+
JwtField.KEY: key_id,
|
|
168
|
+
JwtField.AUD: token_uri,
|
|
169
|
+
JwtField.SUB: client_id,
|
|
170
|
+
JwtField.EXP: datetime.datetime.utcnow() + datetime.timedelta(minutes=60)
|
|
171
|
+
}
|
|
172
|
+
if options and OptionField.CTX in options:
|
|
173
|
+
resolved_ctx = _validate_and_resolve_ctx(options.get(OptionField.CTX))
|
|
174
|
+
if resolved_ctx is not None:
|
|
175
|
+
payload[JwtField.CTX] = resolved_ctx
|
|
176
|
+
try:
|
|
177
|
+
return jwt.encode(payload=payload, key=private_key, algorithm=JWT.ALGORITHM_RS256)
|
|
178
|
+
except Exception:
|
|
179
|
+
raise SkyflowError(SkyflowMessages.Error.JWT_INVALID_FORMAT.value, invalid_input_error_code)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def get_signed_tokens(credentials_obj, options):
|
|
184
|
+
options = options if options is not None else {}
|
|
185
|
+
credentials_obj = _normalize_credentials(credentials_obj)
|
|
186
|
+
expiry_time = int(time.time()) + options.get(OptionField.TIME_TO_LIVE, 60)
|
|
187
|
+
prefix = JWT.SIGNED_TOKEN_PREFIX
|
|
188
|
+
|
|
189
|
+
token_uri = credentials_obj.get(CredentialField.TOKEN_URI)
|
|
190
|
+
if not isinstance(token_uri, str) or not is_valid_url(token_uri):
|
|
191
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_TOKEN_URI.value)
|
|
192
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_TOKEN_URI.value, invalid_input_error_code)
|
|
193
|
+
|
|
194
|
+
resolved_ctx = None
|
|
195
|
+
if OptionField.CTX in options:
|
|
196
|
+
resolved_ctx = _validate_and_resolve_ctx(options[OptionField.CTX])
|
|
197
|
+
|
|
198
|
+
results = []
|
|
199
|
+
if options and options.get(OptionField.DATA_TOKENS):
|
|
200
|
+
for token in options[OptionField.DATA_TOKENS]:
|
|
201
|
+
claims = {
|
|
202
|
+
JwtField.ISS: JWT.ISSUER_SDK,
|
|
203
|
+
JwtField.KEY: credentials_obj.get(CredentialField.KEY_ID),
|
|
204
|
+
JwtField.EXP: expiry_time,
|
|
205
|
+
JwtField.SUB: credentials_obj.get(CredentialField.CLIENT_ID),
|
|
206
|
+
JwtField.TOK: token,
|
|
207
|
+
JwtField.IAT: int(time.time()),
|
|
208
|
+
}
|
|
209
|
+
if resolved_ctx is not None:
|
|
210
|
+
claims[JwtField.CTX] = resolved_ctx
|
|
211
|
+
private_key = credentials_obj.get(CredentialField.PRIVATE_KEY)
|
|
212
|
+
try:
|
|
213
|
+
signed_jwt = jwt.encode(claims, private_key, algorithm=JWT.ALGORITHM_RS256)
|
|
214
|
+
except Exception:
|
|
215
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIALS.value, invalid_input_error_code)
|
|
216
|
+
results.append(get_signed_data_token_response_object(prefix + signed_jwt, token))
|
|
217
|
+
log_info(SkyflowMessages.Info.GET_SIGNED_DATA_TOKEN_SUCCESS.value)
|
|
218
|
+
return results
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def generate_signed_data_tokens(credentials_file_path, options):
|
|
222
|
+
log_info(SkyflowMessages.Info.GET_SIGNED_DATA_TOKENS_TRIGGERED.value)
|
|
223
|
+
try:
|
|
224
|
+
with open(credentials_file_path, 'r') as credentials_file:
|
|
225
|
+
try:
|
|
226
|
+
credentials = json.load(credentials_file)
|
|
227
|
+
except Exception:
|
|
228
|
+
raise SkyflowError(SkyflowMessages.Error.FILE_INVALID_JSON.value.format(credentials_file_path),
|
|
229
|
+
invalid_input_error_code)
|
|
230
|
+
except SkyflowError:
|
|
231
|
+
raise
|
|
232
|
+
except Exception:
|
|
233
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIAL_FILE_PATH.value, invalid_input_error_code)
|
|
234
|
+
return get_signed_tokens(credentials, options)
|
|
235
|
+
|
|
236
|
+
def generate_signed_data_tokens_from_creds(credentials, options):
|
|
237
|
+
log_info(SkyflowMessages.Info.GET_SIGNED_DATA_TOKENS_TRIGGERED.value)
|
|
238
|
+
credentials = credentials.strip()
|
|
239
|
+
try:
|
|
240
|
+
json_credentials = json.loads(credentials.replace('\n', '\\n'))
|
|
241
|
+
except Exception:
|
|
242
|
+
log_error_log(SkyflowMessages.ErrorLogs.INVALID_CREDENTIALS_FILE.value)
|
|
243
|
+
raise SkyflowError(SkyflowMessages.Error.INVALID_CREDENTIALS_STRING.value, invalid_input_error_code)
|
|
244
|
+
return get_signed_tokens(json_credentials, options)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def get_signed_data_token_response_object(signed_token, actual_token):
|
|
248
|
+
return actual_token, signed_token
|
|
File without changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from common.generated.rest.client import SkyflowAuth
|
|
2
|
+
from common.utils.constants import OPTIONAL_TOKEN
|
|
3
|
+
|
|
4
|
+
class AuthClient:
|
|
5
|
+
def __init__(self, url):
|
|
6
|
+
self.__url = url
|
|
7
|
+
self.__api_client = self.initialize_api_client()
|
|
8
|
+
|
|
9
|
+
def initialize_api_client(self):
|
|
10
|
+
return SkyflowAuth(base_url=self.__url, token=OPTIONAL_TOKEN)
|
|
11
|
+
|
|
12
|
+
def get_auth_api(self):
|
|
13
|
+
return self.__api_client.authentication
|
common/utils/__init__.py
ADDED
common/utils/_helpers.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from urllib.parse import urlparse
|
|
2
|
+
|
|
3
|
+
def get_base_url(url):
|
|
4
|
+
parsed_url = urlparse(url)
|
|
5
|
+
base_url = f"{parsed_url.scheme}://{parsed_url.netloc}"
|
|
6
|
+
return base_url
|
|
7
|
+
|
|
8
|
+
def format_scope(scopes):
|
|
9
|
+
if not scopes:
|
|
10
|
+
return None
|
|
11
|
+
return " ".join([f"role:{scope}" for scope in scopes])
|
|
12
|
+
|
|
13
|
+
def is_valid_url(url):
|
|
14
|
+
try:
|
|
15
|
+
result = urlparse(url)
|
|
16
|
+
return all([result.scheme == "https", result.netloc])
|
|
17
|
+
except Exception:
|
|
18
|
+
return False
|