ydb-platform / ydb-python-sdk

YDB Python SDK
https://ydb-platform.github.io/ydb-python-sdk/
Apache License 2.0
87 stars 50 forks source link

dev: StaticCredentials and DriverConfig circular dependency #497

Closed vgvoleg closed 1 month ago

Arhimisha commented 1 month ago

Reproducible example

first_driver_config = ydb.DriverConfig(
    endpoint=endpoint,
    database=database,
    root_certificates=ydb.load_ydb_root_certificate()
)
credentials = ydb.StaticCredentials(
    driver_config=first_driver_config,
    user=ydb_user,
    password=ydb_password
)

# Second driver config with credentials
second_driver_config = ydb.DriverConfig(
    endpoint=endpoint,
    database=database,
    root_certificates=ydb.load_ydb_root_certificate(),
    credentials=credentials
)
# This Driver is work
good_driver = ydb.Driver(driver_config=second_driver_config)
try:
    good_driver.wait(timeout=5)
except Exception:
    print(good_driver.discovery_debug_details())

# This driver don't work 
broken_driver = ydb.Driver(driver_config=first_driver_config, credentials=credentials)
try:
    broken_driver.wait(timeout=5)
except Exception:
    print(broken_driver.discovery_debug_details())

OUT:

Failed to resolve endpoints for database <database>. Endpoint: "<endpoint>". Error details:
 unauthenticated, unauthenticated: { <main>: Error: Access denied without user token }

Installed versions

ydb==3.17.2
vgvoleg commented 1 month ago

Reproducible example

first_driver_config = ydb.DriverConfig(
    endpoint=endpoint,
    database=database,
    root_certificates=ydb.load_ydb_root_certificate()
)
credentials = ydb.StaticCredentials(
    driver_config=first_driver_config,
    user=ydb_user,
    password=ydb_password
)

# Second driver config with credentials
second_driver_config = ydb.DriverConfig(
    endpoint=endpoint,
    database=database,
    root_certificates=ydb.load_ydb_root_certificate(),
    credentials=credentials
)
# This Driver is work
good_driver = ydb.Driver(driver_config=second_driver_config)
try:
    good_driver.wait(timeout=5)
except Exception:
    print(good_driver.discovery_debug_details())

# This driver don't work 
broken_driver = ydb.Driver(driver_config=first_driver_config, credentials=credentials)
try:
    broken_driver.wait(timeout=5)
except Exception:
    print(broken_driver.discovery_debug_details())

OUT:

Failed to resolve endpoints for database <database>. Endpoint: "<endpoint>". Error details:
 unauthenticated, unauthenticated: { <main>: Error: Access denied without user token }

Installed versions

ydb==3.17.2

Hi! As I see in source code we have this behavior: if we pass driver_config to ydb.Driver, Driver will use this driver_config as the only source of truth - that's why credentials from your broken_driver do not work. It is not very obvious, we will think how to make things more straightforward for users. Thanks for feedback, updates will be posted in this issue.

vgvoleg commented 1 month ago

Fixed in https://github.com/ydb-platform/ydb-python-sdk/releases/tag/3.18.1

Now, it is not an issue to create your "broken" driver - arguments from both DriverConfig and Driver will work. Also, there is a new way to create StaticCredentials that looks more straightforward and has no dependency on DriverConfig: ydb.StaticCredentials.from_user_password(user, password) Full example could be found here: https://ydb-platform.github.io/ydb-python-sdk/examples/authentication.html#static-credentials