blob: 1fb1d29f612bac39eaee47bd53b7679a92bfa249 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import tempfile
import unittest
from pathlib import Path
from pypaimon.api.auth.factory import AuthProviderFactory
from pypaimon.api.token_loader import (
DLFLocalFileTokenLoader,
DLFToken,
DLFTokenLoaderFactory,
)
from pypaimon.common.json_util import JSON
from pypaimon.common.options import Options
from pypaimon.common.options.config import CatalogOptions
class DLFLocalFileTokenLoaderTest(unittest.TestCase):
def test_load_token_from_configured_path(self):
token = DLFToken(
access_key_id="access-key-id",
access_key_secret="access-key-secret",
security_token="security-token",
expiration="2099-12-01T12:00:00Z",
)
with tempfile.TemporaryDirectory() as temp_dir:
token_path = Path(temp_dir) / "token.json"
token_path.write_text(JSON.to_json(token), encoding="utf-8")
options = Options({CatalogOptions.DLF_TOKEN_PATH.key(): str(token_path)})
loader = DLFTokenLoaderFactory.create_token_loader(options)
loaded_token = loader.load_token()
self.assertIsInstance(loader, DLFLocalFileTokenLoader)
self.assertEqual(str(token_path), loader.description())
self.assertEqual(token.access_key_id, loaded_token.access_key_id)
self.assertEqual(token.access_key_secret, loaded_token.access_key_secret)
self.assertEqual(token.security_token, loaded_token.security_token)
self.assertEqual(token.expiration, loaded_token.expiration)
def test_auth_provider_uses_token_path_without_explicit_loader(self):
token = DLFToken("access-key-id", "access-key-secret", "security-token")
with tempfile.TemporaryDirectory() as temp_dir:
token_path = Path(temp_dir) / "token.json"
token_path.write_text(JSON.to_json(token), encoding="utf-8")
options = Options({
CatalogOptions.URI.key():
"https://cn-hangzhou-vpc.dlf.aliyuncs.com",
CatalogOptions.TOKEN_PROVIDER.key(): "dlf",
CatalogOptions.DLF_TOKEN_PATH.key(): str(token_path),
})
provider = AuthProviderFactory.create_auth_provider(options)
self.assertIsInstance(provider.token_loader, DLFLocalFileTokenLoader)
self.assertEqual("access-key-id", provider.get_token().access_key_id)
def test_token_loader_takes_precedence_over_static_credentials(self):
file_token = DLFToken("file-ak", "file-sk", "file-sts")
with tempfile.TemporaryDirectory() as temp_dir:
token_path = Path(temp_dir) / "token.json"
token_path.write_text(JSON.to_json(file_token), encoding="utf-8")
for loader_name in (None, "local_file"):
with self.subTest(loader_name=loader_name):
option_values = {
CatalogOptions.URI.key():
"https://cn-hangzhou-vpc.dlf.aliyuncs.com",
CatalogOptions.TOKEN_PROVIDER.key(): "dlf",
CatalogOptions.DLF_TOKEN_PATH.key(): str(token_path),
CatalogOptions.DLF_ACCESS_KEY_ID.key(): "static-ak",
CatalogOptions.DLF_ACCESS_KEY_SECRET.key(): "static-sk",
}
if loader_name is not None:
option_values[
CatalogOptions.DLF_TOKEN_LOADER.key()
] = loader_name
provider = AuthProviderFactory.create_auth_provider(
Options(option_values)
)
self.assertEqual("file-ak", provider.get_token().access_key_id)
def test_unknown_token_loader_does_not_fallback_to_static_credentials(self):
options = Options({
CatalogOptions.URI.key():
"https://cn-hangzhou-vpc.dlf.aliyuncs.com",
CatalogOptions.TOKEN_PROVIDER.key(): "dlf",
CatalogOptions.DLF_TOKEN_LOADER.key(): "unknown",
CatalogOptions.DLF_ACCESS_KEY_ID.key(): "static-ak",
CatalogOptions.DLF_ACCESS_KEY_SECRET.key(): "static-sk",
})
with self.assertRaisesRegex(ValueError, "Unknown DLF token loader: unknown"):
AuthProviderFactory.create_auth_provider(options)
def test_loader_reads_rotated_token(self):
first_token = DLFToken("first-ak", "first-sk", "first-sts")
second_token = DLFToken("second-ak", "second-sk", "second-sts")
with tempfile.TemporaryDirectory() as temp_dir:
token_path = Path(temp_dir) / "token.json"
token_path.write_text(JSON.to_json(first_token), encoding="utf-8")
loader = DLFLocalFileTokenLoader(str(token_path))
self.assertEqual("first-ak", loader.load_token().access_key_id)
token_path.write_text(JSON.to_json(second_token), encoding="utf-8")
self.assertEqual("second-ak", loader.load_token().access_key_id)
def test_auth_provider_reloads_expiring_token_from_path(self):
expired_token = DLFToken(
"first-ak", "first-sk", "first-sts", "2000-01-01T00:00:00Z"
)
fresh_token = DLFToken(
"second-ak", "second-sk", "second-sts", "2099-01-01T00:00:00Z"
)
with tempfile.TemporaryDirectory() as temp_dir:
token_path = Path(temp_dir) / "token.json"
token_path.write_text(JSON.to_json(expired_token), encoding="utf-8")
options = Options({
CatalogOptions.URI.key():
"https://cn-hangzhou-vpc.dlf.aliyuncs.com",
CatalogOptions.TOKEN_PROVIDER.key(): "dlf",
CatalogOptions.DLF_TOKEN_PATH.key(): str(token_path),
})
provider = AuthProviderFactory.create_auth_provider(options)
self.assertEqual("first-ak", provider.get_token().access_key_id)
token_path.write_text(JSON.to_json(fresh_token), encoding="utf-8")
self.assertEqual("second-ak", provider.get_token().access_key_id)
def test_malformed_token_file_does_not_leak_credentials(self):
secret = "STSSECRET_AKID_9999"
with tempfile.TemporaryDirectory() as temp_dir:
token_path = Path(temp_dir) / "token.json"
token_path.write_text(
'{"AccessKeyId":"akid","AccessKeySecret":"%s" INVALID_JSON'
% secret,
encoding="utf-8",
)
with self.assertRaisesRegex(RuntimeError, "Failed to parse token file") as ctx:
DLFLocalFileTokenLoader.read_token(str(token_path), max_retries=1)
self.assertNotIn(secret, str(ctx.exception))
self.assertIsNone(ctx.exception.__cause__)
if __name__ == "__main__":
unittest.main()