Skip to content

Commit 23369d9

Browse files
authored
Hide service connector secrets as internal implementation details (#3770)
* WIP * Hide service connector secrets from users * Add secret expansion option to service connector API calls and endpoints * Renamed hidden to internal in the secrets table * Fix secret serialization and other minor bugs * Fix linter and spelling errors and add DB migration file * Validate connector models in the REST zen store and fix unit tests * Fix unit tests * Fix remaining unit tests * Fix linter errors
1 parent 234e352 commit 23369d9

File tree

20 files changed

+1027
-674
lines changed

20 files changed

+1027
-674
lines changed

.typos.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ arange = "arange"
3636
cachable = "cachable"
3737
OT = "OT"
3838
cll = "cll"
39+
ser = "ser"
3940

4041
[default]
4142
locale = "en-us"

src/zenml/cli/service_connectors.py

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -899,14 +899,7 @@ def register_service_connector(
899899

900900
# Prepare the rest of the variables to fall through to the
901901
# non-interactive configuration case
902-
parsed_args = connector_model.configuration
903-
parsed_args.update(
904-
{
905-
k: s.get_secret_value()
906-
for k, s in connector_model.secrets.items()
907-
if s is not None
908-
}
909-
)
902+
parsed_args = connector_model.configuration.plain
910903
auto_configure = False
911904
no_verify = False
912905
expiration_seconds = connector_model.expiration_seconds
@@ -1137,7 +1130,7 @@ def describe_service_connector(
11371130
try:
11381131
connector = client.get_service_connector(
11391132
name_id_or_prefix=name_id_or_prefix,
1140-
load_secrets=True,
1133+
expand_secrets=show_secrets,
11411134
)
11421135
except KeyError as err:
11431136
cli_utils.error(str(err))
@@ -1385,7 +1378,7 @@ def update_service_connector(
13851378
connector = client.get_service_connector(
13861379
name_id_or_prefix,
13871380
allow_name_prefix_match=False,
1388-
load_secrets=True,
1381+
expand_secrets=True,
13891382
)
13901383
except KeyError as e:
13911384
cli_utils.error(str(e))
@@ -1445,7 +1438,7 @@ def update_service_connector(
14451438
default=False,
14461439
)
14471440

1448-
existing_config = connector.full_configuration
1441+
existing_config = connector.configuration.plain
14491442

14501443
if confirm:
14511444
# Here we reconfigure the connector or update the existing
@@ -1582,7 +1575,7 @@ def update_service_connector(
15821575
# Non-interactive configuration
15831576

15841577
# Apply the configuration from the command line arguments
1585-
config_dict = connector.full_configuration
1578+
config_dict = connector.configuration.plain
15861579
config_dict.update(parsed_args)
15871580

15881581
if not resource_type and not connector.is_multi_type:

src/zenml/cli/stack.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1783,14 +1783,10 @@ def _get_service_connector_info(
17831783
answers = {}
17841784
for req_field in required_fields:
17851785
if connector_details:
1786-
if conf_value := connector_details.configuration.get(
1786+
if conf_value := connector_details.configuration.get_plain(
17871787
req_field, None
17881788
):
17891789
answers[req_field] = conf_value
1790-
elif secret_value := connector_details.secrets.get(
1791-
req_field, None
1792-
):
1793-
answers[req_field] = secret_value.get_secret_value()
17941790
if req_field not in answers:
17951791
answers[req_field] = Prompt.ask(
17961792
f"Please enter value for `{req_field}`:",

src/zenml/cli/utils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,7 +1814,7 @@ def print_service_connector_configuration(
18141814

18151815
console.print(rich_table)
18161816

1817-
if len(connector.configuration) == 0 and len(connector.secrets) == 0:
1817+
if len(connector.configuration) == 0:
18181818
declare("No configuration options are set for this connector.")
18191819

18201820
else:
@@ -1826,8 +1826,8 @@ def print_service_connector_configuration(
18261826
rich_table.add_column("PROPERTY")
18271827
rich_table.add_column("VALUE", overflow="fold")
18281828

1829-
config = connector.configuration.copy()
1830-
secrets = connector.secrets.copy()
1829+
config = connector.configuration.non_secrets
1830+
secrets = connector.configuration.secrets
18311831
for key, value in secrets.items():
18321832
if not show_secrets:
18331833
config[key] = "[HIDDEN]"

src/zenml/client.py

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -5500,17 +5500,18 @@ def get_service_connector(
55005500
self,
55015501
name_id_or_prefix: Union[str, UUID],
55025502
allow_name_prefix_match: bool = True,
5503-
load_secrets: bool = False,
55045503
hydrate: bool = True,
5504+
expand_secrets: bool = False,
55055505
) -> ServiceConnectorResponse:
55065506
"""Fetches a registered service connector.
55075507
55085508
Args:
55095509
name_id_or_prefix: The id of the service connector to fetch.
55105510
allow_name_prefix_match: If True, allow matching by name prefix.
5511-
load_secrets: If True, load the secrets for the service connector.
55125511
hydrate: Flag deciding whether to hydrate the output model(s)
55135512
by including metadata fields in the response.
5513+
expand_secrets: If True, expand the secrets for the service
5514+
connector.
55145515
55155516
Returns:
55165517
The registered service connector.
@@ -5521,25 +5522,9 @@ def get_service_connector(
55215522
name_id_or_prefix=name_id_or_prefix,
55225523
allow_name_prefix_match=allow_name_prefix_match,
55235524
hydrate=hydrate,
5525+
expand_secrets=expand_secrets,
55245526
)
55255527

5526-
if load_secrets and connector.secret_id:
5527-
client = Client()
5528-
try:
5529-
secret = client.get_secret(
5530-
name_id_or_prefix=connector.secret_id,
5531-
allow_partial_id_match=False,
5532-
allow_partial_name_match=False,
5533-
)
5534-
except KeyError as err:
5535-
logger.error(
5536-
"Unable to retrieve secret values associated with "
5537-
f"service connector '{connector.name}': {err}"
5538-
)
5539-
else:
5540-
# Add secret values to connector configuration
5541-
connector.secrets.update(secret.values)
5542-
55435528
return connector
55445529

55455530
def list_service_connectors(
@@ -5558,8 +5543,8 @@ def list_service_connectors(
55585543
resource_id: Optional[str] = None,
55595544
user: Optional[Union[UUID, str]] = None,
55605545
labels: Optional[Dict[str, Optional[str]]] = None,
5561-
secret_id: Optional[Union[str, UUID]] = None,
55625546
hydrate: bool = False,
5547+
expand_secrets: bool = False,
55635548
) -> Page[ServiceConnectorResponse]:
55645549
"""Lists all registered service connectors.
55655550
@@ -5580,10 +5565,10 @@ def list_service_connectors(
55805565
user: Filter by user name/ID.
55815566
name: The name of the service connector to filter by.
55825567
labels: The labels of the service connector to filter by.
5583-
secret_id: Filter by the id of the secret that is referenced by the
5584-
service connector.
55855568
hydrate: Flag deciding whether to hydrate the output model(s)
55865569
by including metadata fields in the response.
5570+
expand_secrets: If True, expand the secrets for the service
5571+
connectors.
55875572
55885573
Returns:
55895574
A page of service connectors.
@@ -5603,11 +5588,11 @@ def list_service_connectors(
56035588
created=created,
56045589
updated=updated,
56055590
labels=labels,
5606-
secret_id=secret_id,
56075591
)
56085592
return self.zen_store.list_service_connectors(
56095593
filter_model=connector_filter_model,
56105594
hydrate=hydrate,
5595+
expand_secrets=expand_secrets,
56115596
)
56125597

56135598
def update_service_connector(
@@ -5691,7 +5676,9 @@ def update_service_connector(
56915676
connector_model = self.get_service_connector(
56925677
name_id_or_prefix,
56935678
allow_name_prefix_match=False,
5694-
load_secrets=True,
5679+
# We need the existing secrets only if a new configuration is not
5680+
# provided.
5681+
expand_secrets=configuration is None,
56955682
)
56965683

56975684
connector_instance: Optional[ServiceConnector] = None
@@ -5736,23 +5723,16 @@ def update_service_connector(
57365723
)
57375724

57385725
# Validate and configure the resources
5739-
if configuration is not None:
5726+
connector_update.validate_and_configure_resources(
5727+
connector_type=connector,
5728+
resource_types=resource_types,
5729+
resource_id=resource_id,
57405730
# The supplied configuration is a drop-in replacement for the
5741-
# existing configuration and secrets
5742-
connector_update.validate_and_configure_resources(
5743-
connector_type=connector,
5744-
resource_types=resource_types,
5745-
resource_id=resource_id,
5746-
configuration=configuration,
5747-
)
5748-
else:
5749-
connector_update.validate_and_configure_resources(
5750-
connector_type=connector,
5751-
resource_types=resource_types,
5752-
resource_id=resource_id,
5753-
configuration=connector_model.configuration,
5754-
secrets=connector_model.secrets,
5755-
)
5731+
# existing configuration
5732+
configuration=configuration
5733+
if configuration is not None
5734+
else connector_model.configuration,
5735+
)
57565736

57575737
# Add the labels
57585738
if labels is not None:
@@ -5912,6 +5892,12 @@ def verify_service_connector(
59125892
list_resources=list_resources,
59135893
)
59145894
else:
5895+
# Get the service connector model, with full secrets
5896+
service_connector = self.get_service_connector(
5897+
name_id_or_prefix=name_id_or_prefix,
5898+
allow_name_prefix_match=False,
5899+
expand_secrets=True,
5900+
)
59155901
connector_instance = (
59165902
service_connector_registry.instantiate_connector(
59175903
model=service_connector
@@ -6034,6 +6020,12 @@ def get_service_connector_client(
60346020
# server-side implementation may not be able to do so
60356021
connector_client.verify()
60366022
else:
6023+
# Get the service connector model, with full secrets
6024+
service_connector = self.get_service_connector(
6025+
name_id_or_prefix=name_id_or_prefix,
6026+
allow_name_prefix_match=False,
6027+
expand_secrets=True,
6028+
)
60376029
connector_instance = (
60386030
service_connector_registry.instantiate_connector(
60396031
model=service_connector

src/zenml/integrations/aws/container_registries/aws_container_registry.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ def _get_ecr_client(self) -> BaseClient:
8181
"""
8282
if self.connector:
8383
try:
84-
model = Client().get_service_connector(self.connector)
84+
model = Client().get_service_connector(
85+
self.connector, expand_secrets=True
86+
)
8587
connector = service_connector_registry.instantiate_connector(
8688
model=model
8789
)

src/zenml/models/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,7 @@
286286
ServiceAccountResponse,
287287
)
288288
from zenml.models.v2.core.service_connector import (
289+
ServiceConnectorConfiguration,
289290
ServiceConnectorRequest,
290291
ServiceConnectorUpdate,
291292
ServiceConnectorFilter,
@@ -739,6 +740,7 @@
739740
"ServiceAccountUpdate",
740741
"ServiceAccountRequest",
741742
"ServiceAccountResponse",
743+
"ServiceConnectorConfiguration",
742744
"ServiceConnectorRequest",
743745
"ServiceConnectorUpdate",
744746
"ServiceConnectorFilter",

0 commit comments

Comments
 (0)