zenml-nightly 0.75.0.dev20250312__py3-none-any.whl → 0.75.0.dev20250314__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.
- zenml/VERSION +1 -1
- zenml/__init__.py +2 -0
- zenml/analytics/context.py +7 -0
- zenml/analytics/enums.py +2 -2
- zenml/artifacts/utils.py +2 -4
- zenml/cli/__init__.py +8 -9
- zenml/cli/base.py +2 -2
- zenml/cli/code_repository.py +1 -1
- zenml/cli/login.py +6 -0
- zenml/cli/model.py +7 -15
- zenml/cli/pipeline.py +3 -3
- zenml/cli/project.py +172 -0
- zenml/cli/secret.py +47 -44
- zenml/cli/service_accounts.py +0 -1
- zenml/cli/service_connectors.py +15 -17
- zenml/cli/stack.py +0 -3
- zenml/cli/stack_components.py +2 -2
- zenml/cli/tag.py +3 -5
- zenml/cli/utils.py +25 -23
- zenml/client.py +749 -475
- zenml/config/global_config.py +48 -37
- zenml/config/pipeline_configurations.py +3 -2
- zenml/config/pipeline_run_configuration.py +2 -1
- zenml/config/secret_reference_mixin.py +1 -1
- zenml/constants.py +6 -6
- zenml/enums.py +0 -7
- zenml/event_hub/event_hub.py +3 -1
- zenml/exceptions.py +0 -24
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +5 -3
- zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +1 -4
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +7 -6
- zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +1 -4
- zenml/integrations/mlflow/steps/mlflow_registry.py +3 -3
- zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -1
- zenml/integrations/wandb/__init__.py +1 -1
- zenml/integrations/wandb/experiment_trackers/wandb_experiment_tracker.py +29 -9
- zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +5 -3
- zenml/model/model.py +10 -10
- zenml/model_registries/base_model_registry.py +1 -1
- zenml/models/__init__.py +45 -28
- zenml/models/v2/base/base.py +0 -5
- zenml/models/v2/base/filter.py +2 -2
- zenml/models/v2/base/scoped.py +135 -156
- zenml/models/v2/core/action.py +12 -12
- zenml/models/v2/core/api_key.py +1 -1
- zenml/models/v2/core/artifact.py +31 -18
- zenml/models/v2/core/artifact_version.py +57 -40
- zenml/models/v2/core/code_repository.py +12 -12
- zenml/models/v2/core/component.py +22 -33
- zenml/models/v2/core/device.py +3 -2
- zenml/models/v2/core/event_source.py +14 -14
- zenml/models/v2/core/flavor.py +19 -47
- zenml/models/v2/core/logs.py +1 -2
- zenml/models/v2/core/model.py +23 -20
- zenml/models/v2/core/model_version.py +51 -42
- zenml/models/v2/core/pipeline.py +16 -16
- zenml/models/v2/core/pipeline_build.py +14 -14
- zenml/models/v2/core/pipeline_deployment.py +12 -14
- zenml/models/v2/core/pipeline_run.py +21 -29
- zenml/models/v2/core/project.py +203 -0
- zenml/models/v2/core/run_metadata.py +2 -2
- zenml/models/v2/core/run_template.py +16 -17
- zenml/models/v2/core/schedule.py +12 -21
- zenml/models/v2/core/secret.py +94 -128
- zenml/models/v2/core/server_settings.py +2 -2
- zenml/models/v2/core/service.py +57 -26
- zenml/models/v2/core/service_connector.py +14 -16
- zenml/models/v2/core/stack.py +24 -26
- zenml/models/v2/core/step_run.py +16 -28
- zenml/models/v2/core/tag.py +41 -15
- zenml/models/v2/core/trigger.py +13 -13
- zenml/models/v2/core/trigger_execution.py +2 -2
- zenml/models/v2/core/user.py +2 -2
- zenml/models/v2/misc/statistics.py +45 -0
- zenml/models/v2/misc/tag.py +27 -0
- zenml/orchestrators/cache_utils.py +7 -7
- zenml/orchestrators/input_utils.py +1 -0
- zenml/orchestrators/step_launcher.py +1 -2
- zenml/orchestrators/step_run_utils.py +2 -4
- zenml/orchestrators/step_runner.py +10 -1
- zenml/orchestrators/utils.py +4 -4
- zenml/pipelines/build_utils.py +2 -4
- zenml/pipelines/pipeline_decorator.py +3 -2
- zenml/pipelines/pipeline_definition.py +8 -9
- zenml/pipelines/run_utils.py +4 -4
- zenml/service_connectors/service_connector.py +0 -10
- zenml/service_connectors/service_connector_utils.py +0 -2
- zenml/stack/authentication_mixin.py +1 -1
- zenml/stack/flavor.py +3 -14
- zenml/stack/stack.py +0 -1
- zenml/stack/stack_component.py +1 -5
- zenml/steps/base_step.py +10 -2
- zenml/steps/step_context.py +19 -0
- zenml/utils/string_utils.py +1 -1
- zenml/utils/tag_utils.py +642 -0
- zenml/zen_server/cloud_utils.py +21 -0
- zenml/zen_server/exceptions.py +0 -6
- zenml/zen_server/rbac/endpoint_utils.py +134 -46
- zenml/zen_server/rbac/models.py +65 -3
- zenml/zen_server/rbac/rbac_interface.py +9 -0
- zenml/zen_server/rbac/rbac_sql_zen_store.py +15 -7
- zenml/zen_server/rbac/utils.py +155 -30
- zenml/zen_server/rbac/zenml_cloud_rbac.py +39 -11
- zenml/zen_server/routers/actions_endpoints.py +3 -5
- zenml/zen_server/routers/artifact_endpoint.py +0 -5
- zenml/zen_server/routers/artifact_version_endpoints.py +15 -9
- zenml/zen_server/routers/auth_endpoints.py +22 -7
- zenml/zen_server/routers/code_repositories_endpoints.py +54 -3
- zenml/zen_server/routers/devices_endpoints.py +0 -4
- zenml/zen_server/routers/event_source_endpoints.py +0 -5
- zenml/zen_server/routers/flavors_endpoints.py +0 -5
- zenml/zen_server/routers/logs_endpoints.py +0 -1
- zenml/zen_server/routers/model_versions_endpoints.py +100 -23
- zenml/zen_server/routers/models_endpoints.py +50 -69
- zenml/zen_server/routers/pipeline_builds_endpoints.py +55 -3
- zenml/zen_server/routers/pipeline_deployments_endpoints.py +56 -4
- zenml/zen_server/routers/pipelines_endpoints.py +70 -3
- zenml/zen_server/routers/plugin_endpoints.py +0 -1
- zenml/zen_server/routers/projects_endpoints.py +283 -0
- zenml/zen_server/routers/run_metadata_endpoints.py +97 -0
- zenml/zen_server/routers/run_templates_endpoints.py +64 -3
- zenml/zen_server/routers/runs_endpoints.py +58 -8
- zenml/zen_server/routers/schedule_endpoints.py +67 -6
- zenml/zen_server/routers/secrets_endpoints.py +38 -4
- zenml/zen_server/routers/server_endpoints.py +53 -1
- zenml/zen_server/routers/service_accounts_endpoints.py +14 -15
- zenml/zen_server/routers/service_connectors_endpoints.py +94 -14
- zenml/zen_server/routers/service_endpoints.py +18 -7
- zenml/zen_server/routers/stack_components_endpoints.py +66 -7
- zenml/zen_server/routers/stacks_endpoints.py +95 -6
- zenml/zen_server/routers/steps_endpoints.py +17 -11
- zenml/zen_server/routers/tag_resource_endpoints.py +115 -0
- zenml/zen_server/routers/tags_endpoints.py +6 -17
- zenml/zen_server/routers/triggers_endpoints.py +5 -8
- zenml/zen_server/routers/users_endpoints.py +9 -12
- zenml/zen_server/template_execution/utils.py +8 -7
- zenml/zen_server/utils.py +21 -0
- zenml/zen_server/zen_server_api.py +7 -2
- zenml/zen_stores/base_zen_store.py +50 -69
- zenml/zen_stores/migrations/versions/12eff0206201_rename_workspace_to_project.py +768 -0
- zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +20 -10
- zenml/zen_stores/migrations/versions/1f9d1cd00b90_add_unique_name_constraints.py +231 -0
- zenml/zen_stores/migrations/versions/288f4fb6e112_make_tags_user_scoped.py +74 -0
- zenml/zen_stores/migrations/versions/2e695a26fe7a_add_user_default_workspace.py +45 -0
- zenml/zen_stores/migrations/versions/3b1776345020_remove_workspace_from_globals.py +81 -0
- zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +136 -0
- zenml/zen_stores/migrations/versions/9e7bf0970266_adding_exclusive_attribute_to_tags.py +47 -0
- zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +8 -4
- zenml/zen_stores/migrations/versions/cbc6acd71f92_add_workspace_display_name.py +58 -0
- zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +12 -6
- zenml/zen_stores/migrations/versions/f1d723fd723b_add_secret_private_attr.py +61 -0
- zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py +35 -0
- zenml/zen_stores/rest_zen_store.py +223 -230
- zenml/zen_stores/schemas/__init__.py +2 -2
- zenml/zen_stores/schemas/action_schemas.py +15 -8
- zenml/zen_stores/schemas/api_key_schemas.py +8 -1
- zenml/zen_stores/schemas/artifact_schemas.py +35 -10
- zenml/zen_stores/schemas/code_repository_schemas.py +22 -17
- zenml/zen_stores/schemas/component_schemas.py +9 -14
- zenml/zen_stores/schemas/event_source_schemas.py +15 -8
- zenml/zen_stores/schemas/flavor_schemas.py +14 -20
- zenml/zen_stores/schemas/model_schemas.py +18 -17
- zenml/zen_stores/schemas/pipeline_build_schemas.py +7 -7
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +10 -8
- zenml/zen_stores/schemas/pipeline_run_schemas.py +9 -12
- zenml/zen_stores/schemas/pipeline_schemas.py +9 -9
- zenml/zen_stores/schemas/{workspace_schemas.py → project_schemas.py} +53 -65
- zenml/zen_stores/schemas/run_metadata_schemas.py +5 -5
- zenml/zen_stores/schemas/run_template_schemas.py +17 -13
- zenml/zen_stores/schemas/schedule_schema.py +16 -21
- zenml/zen_stores/schemas/secret_schemas.py +15 -25
- zenml/zen_stores/schemas/service_connector_schemas.py +8 -17
- zenml/zen_stores/schemas/service_schemas.py +7 -8
- zenml/zen_stores/schemas/stack_schemas.py +12 -15
- zenml/zen_stores/schemas/step_run_schemas.py +14 -15
- zenml/zen_stores/schemas/tag_schemas.py +30 -2
- zenml/zen_stores/schemas/trigger_schemas.py +15 -8
- zenml/zen_stores/schemas/user_schemas.py +12 -2
- zenml/zen_stores/schemas/utils.py +16 -0
- zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +0 -3
- zenml/zen_stores/sql_zen_store.py +2984 -2369
- zenml/zen_stores/template_utils.py +1 -1
- zenml/zen_stores/zen_store_interface.py +136 -126
- {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/METADATA +1 -1
- {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/RECORD +188 -173
- zenml/cli/workspace.py +0 -86
- zenml/models/v2/core/workspace.py +0 -131
- zenml/zen_server/routers/workspaces_endpoints.py +0 -1469
- {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/entry_points.txt +0 -0
zenml/zen_server/rbac/utils.py
CHANGED
@@ -32,6 +32,8 @@ from zenml.exceptions import IllegalOperationError
|
|
32
32
|
from zenml.models import (
|
33
33
|
BaseIdentifiedResponse,
|
34
34
|
Page,
|
35
|
+
ProjectScopedRequest,
|
36
|
+
ProjectScopedResponse,
|
35
37
|
UserResponse,
|
36
38
|
UserScopedResponse,
|
37
39
|
)
|
@@ -55,24 +57,39 @@ def dehydrate_page(page: Page[AnyResponse]) -> Page[AnyResponse]:
|
|
55
57
|
Returns:
|
56
58
|
The page with (potentially) dehydrated items.
|
57
59
|
"""
|
60
|
+
new_items = dehydrate_response_model_batch(page.items)
|
61
|
+
return page.model_copy(update={"items": new_items})
|
62
|
+
|
63
|
+
|
64
|
+
def dehydrate_response_model_batch(
|
65
|
+
batch: List[AnyResponse],
|
66
|
+
) -> List[AnyResponse]:
|
67
|
+
"""Dehydrate all items of a batch.
|
68
|
+
|
69
|
+
Args:
|
70
|
+
batch: The batch to dehydrate.
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
The batch with (potentially) dehydrated items.
|
74
|
+
"""
|
58
75
|
if not server_config().rbac_enabled:
|
59
|
-
return
|
76
|
+
return batch
|
60
77
|
|
61
78
|
auth_context = get_auth_context()
|
62
79
|
assert auth_context
|
63
80
|
|
64
|
-
resource_list = [get_subresources_for_model(item) for item in
|
81
|
+
resource_list = [get_subresources_for_model(item) for item in batch]
|
65
82
|
resources = set.union(*resource_list) if resource_list else set()
|
66
83
|
permissions = rbac().check_permissions(
|
67
84
|
user=auth_context.user, resources=resources, action=Action.READ
|
68
85
|
)
|
69
86
|
|
70
|
-
|
87
|
+
new_batch = [
|
71
88
|
dehydrate_response_model(item, permissions=permissions)
|
72
|
-
for item in
|
89
|
+
for item in batch
|
73
90
|
]
|
74
91
|
|
75
|
-
return
|
92
|
+
return new_batch
|
76
93
|
|
77
94
|
|
78
95
|
def dehydrate_response_model(
|
@@ -161,7 +178,7 @@ def _dehydrate_value(
|
|
161
178
|
return value
|
162
179
|
|
163
180
|
|
164
|
-
def has_permissions_for_model(model:
|
181
|
+
def has_permissions_for_model(model: AnyModel, action: Action) -> bool:
|
165
182
|
"""If the active user has permissions to perform the action on the model.
|
166
183
|
|
167
184
|
Args:
|
@@ -201,7 +218,7 @@ def get_permission_denied_model(model: AnyResponse) -> AnyResponse:
|
|
201
218
|
|
202
219
|
|
203
220
|
def batch_verify_permissions_for_models(
|
204
|
-
models: Sequence[
|
221
|
+
models: Sequence[AnyModel],
|
205
222
|
action: Action,
|
206
223
|
) -> None:
|
207
224
|
"""Batch permission verification for models.
|
@@ -229,7 +246,7 @@ def batch_verify_permissions_for_models(
|
|
229
246
|
batch_verify_permissions(resources=resources, action=action)
|
230
247
|
|
231
248
|
|
232
|
-
def verify_permission_for_model(model:
|
249
|
+
def verify_permission_for_model(model: AnyModel, action: Action) -> None:
|
233
250
|
"""Verifies if a user has permission to perform an action on a model.
|
234
251
|
|
235
252
|
Args:
|
@@ -283,6 +300,7 @@ def verify_permission(
|
|
283
300
|
resource_type: str,
|
284
301
|
action: Action,
|
285
302
|
resource_id: Optional[UUID] = None,
|
303
|
+
project_id: Optional[UUID] = None,
|
286
304
|
) -> None:
|
287
305
|
"""Verifies if a user has permission to perform an action on a resource.
|
288
306
|
|
@@ -291,20 +309,27 @@ def verify_permission(
|
|
291
309
|
action on.
|
292
310
|
action: The action the user wants to perform.
|
293
311
|
resource_id: ID of the resource the user wants to perform the action on.
|
312
|
+
project_id: ID of the project the user wants to perform the action
|
313
|
+
on. Only used for project scoped resources.
|
294
314
|
"""
|
295
|
-
resource = Resource(
|
315
|
+
resource = Resource(
|
316
|
+
type=resource_type, id=resource_id, project_id=project_id
|
317
|
+
)
|
296
318
|
batch_verify_permissions(resources={resource}, action=action)
|
297
319
|
|
298
320
|
|
299
321
|
def get_allowed_resource_ids(
|
300
322
|
resource_type: str,
|
301
323
|
action: Action = Action.READ,
|
324
|
+
project_id: Optional[UUID] = None,
|
302
325
|
) -> Optional[Set[UUID]]:
|
303
326
|
"""Get all resource IDs of a resource type that a user can access.
|
304
327
|
|
305
328
|
Args:
|
306
329
|
resource_type: The resource type.
|
307
330
|
action: The action the user wants to perform on the resource.
|
331
|
+
project_id: Optional project ID to filter the resources by.
|
332
|
+
Required for project scoped resources.
|
308
333
|
|
309
334
|
Returns:
|
310
335
|
A list of resource IDs or `None` if the user has full access to the
|
@@ -321,7 +346,7 @@ def get_allowed_resource_ids(
|
|
321
346
|
allowed_ids,
|
322
347
|
) = rbac().list_allowed_resource_ids(
|
323
348
|
user=auth_context.user,
|
324
|
-
resource=Resource(type=resource_type),
|
349
|
+
resource=Resource(type=resource_type, project_id=project_id),
|
325
350
|
action=action,
|
326
351
|
)
|
327
352
|
|
@@ -331,7 +356,7 @@ def get_allowed_resource_ids(
|
|
331
356
|
return {UUID(id) for id in allowed_ids}
|
332
357
|
|
333
358
|
|
334
|
-
def get_resource_for_model(model:
|
359
|
+
def get_resource_for_model(model: AnyModel) -> Optional[Resource]:
|
335
360
|
"""Get the resource associated with a model object.
|
336
361
|
|
337
362
|
Args:
|
@@ -346,12 +371,24 @@ def get_resource_for_model(model: AnyResponse) -> Optional[Resource]:
|
|
346
371
|
# This model is not tied to any RBAC resource type
|
347
372
|
return None
|
348
373
|
|
349
|
-
|
374
|
+
project_id: Optional[UUID] = None
|
375
|
+
if isinstance(model, ProjectScopedResponse):
|
376
|
+
# A project scoped response is always scoped to a specific project
|
377
|
+
project_id = model.project.id
|
378
|
+
elif isinstance(model, ProjectScopedRequest):
|
379
|
+
# A project scoped request is always scoped to a specific project
|
380
|
+
project_id = model.project
|
381
|
+
|
382
|
+
resource_id: Optional[UUID] = None
|
383
|
+
if isinstance(model, BaseIdentifiedResponse):
|
384
|
+
resource_id = model.id
|
385
|
+
|
386
|
+
return Resource(type=resource_type, id=resource_id, project_id=project_id)
|
350
387
|
|
351
388
|
|
352
389
|
def get_surrogate_permission_model_for_model(
|
353
|
-
model:
|
354
|
-
) ->
|
390
|
+
model: BaseModel, action: str
|
391
|
+
) -> BaseModel:
|
355
392
|
"""Get a surrogate permission model for a model.
|
356
393
|
|
357
394
|
In some cases a different model instead of the original model is used to
|
@@ -379,7 +416,7 @@ def get_surrogate_permission_model_for_model(
|
|
379
416
|
|
380
417
|
|
381
418
|
def get_resource_type_for_model(
|
382
|
-
model:
|
419
|
+
model: AnyModel,
|
383
420
|
) -> Optional[ResourceType]:
|
384
421
|
"""Get the resource type associated with a model object.
|
385
422
|
|
@@ -391,27 +428,52 @@ def get_resource_type_for_model(
|
|
391
428
|
is not associated with any resource type.
|
392
429
|
"""
|
393
430
|
from zenml.models import (
|
431
|
+
ActionRequest,
|
394
432
|
ActionResponse,
|
433
|
+
ArtifactRequest,
|
395
434
|
ArtifactResponse,
|
435
|
+
ArtifactVersionRequest,
|
396
436
|
ArtifactVersionResponse,
|
437
|
+
CodeRepositoryRequest,
|
397
438
|
CodeRepositoryResponse,
|
439
|
+
ComponentRequest,
|
398
440
|
ComponentResponse,
|
441
|
+
EventSourceRequest,
|
399
442
|
EventSourceResponse,
|
443
|
+
FlavorRequest,
|
400
444
|
FlavorResponse,
|
445
|
+
ModelRequest,
|
401
446
|
ModelResponse,
|
447
|
+
ModelVersionRequest,
|
402
448
|
ModelVersionResponse,
|
449
|
+
PipelineBuildRequest,
|
403
450
|
PipelineBuildResponse,
|
451
|
+
PipelineDeploymentRequest,
|
404
452
|
PipelineDeploymentResponse,
|
453
|
+
PipelineRequest,
|
405
454
|
PipelineResponse,
|
455
|
+
PipelineRunRequest,
|
406
456
|
PipelineRunResponse,
|
457
|
+
ProjectRequest,
|
458
|
+
ProjectResponse,
|
459
|
+
RunMetadataRequest,
|
460
|
+
RunTemplateRequest,
|
407
461
|
RunTemplateResponse,
|
462
|
+
SecretRequest,
|
408
463
|
SecretResponse,
|
464
|
+
ServiceAccountRequest,
|
409
465
|
ServiceAccountResponse,
|
466
|
+
ServiceConnectorRequest,
|
410
467
|
ServiceConnectorResponse,
|
468
|
+
ServiceRequest,
|
411
469
|
ServiceResponse,
|
470
|
+
StackRequest,
|
412
471
|
StackResponse,
|
472
|
+
TagRequest,
|
413
473
|
TagResponse,
|
474
|
+
TriggerExecutionRequest,
|
414
475
|
TriggerExecutionResponse,
|
476
|
+
TriggerRequest,
|
415
477
|
TriggerResponse,
|
416
478
|
)
|
417
479
|
|
@@ -419,36 +481,60 @@ def get_resource_type_for_model(
|
|
419
481
|
Any,
|
420
482
|
ResourceType,
|
421
483
|
] = {
|
484
|
+
ActionRequest: ResourceType.ACTION,
|
422
485
|
ActionResponse: ResourceType.ACTION,
|
486
|
+
ArtifactRequest: ResourceType.ARTIFACT,
|
487
|
+
ArtifactResponse: ResourceType.ARTIFACT,
|
488
|
+
ArtifactVersionRequest: ResourceType.ARTIFACT_VERSION,
|
489
|
+
ArtifactVersionResponse: ResourceType.ARTIFACT_VERSION,
|
490
|
+
CodeRepositoryRequest: ResourceType.CODE_REPOSITORY,
|
491
|
+
CodeRepositoryResponse: ResourceType.CODE_REPOSITORY,
|
492
|
+
ComponentRequest: ResourceType.STACK_COMPONENT,
|
493
|
+
ComponentResponse: ResourceType.STACK_COMPONENT,
|
494
|
+
EventSourceRequest: ResourceType.EVENT_SOURCE,
|
423
495
|
EventSourceResponse: ResourceType.EVENT_SOURCE,
|
496
|
+
FlavorRequest: ResourceType.FLAVOR,
|
424
497
|
FlavorResponse: ResourceType.FLAVOR,
|
425
|
-
|
426
|
-
ComponentResponse: ResourceType.STACK_COMPONENT,
|
427
|
-
StackResponse: ResourceType.STACK,
|
428
|
-
PipelineResponse: ResourceType.PIPELINE,
|
429
|
-
CodeRepositoryResponse: ResourceType.CODE_REPOSITORY,
|
430
|
-
SecretResponse: ResourceType.SECRET,
|
498
|
+
ModelRequest: ResourceType.MODEL,
|
431
499
|
ModelResponse: ResourceType.MODEL,
|
500
|
+
ModelVersionRequest: ResourceType.MODEL_VERSION,
|
432
501
|
ModelVersionResponse: ResourceType.MODEL_VERSION,
|
433
|
-
|
434
|
-
ArtifactVersionResponse: ResourceType.ARTIFACT_VERSION,
|
435
|
-
# WorkspaceResponse: ResourceType.WORKSPACE,
|
436
|
-
# UserResponse: ResourceType.USER,
|
437
|
-
PipelineDeploymentResponse: ResourceType.PIPELINE_DEPLOYMENT,
|
502
|
+
PipelineBuildRequest: ResourceType.PIPELINE_BUILD,
|
438
503
|
PipelineBuildResponse: ResourceType.PIPELINE_BUILD,
|
504
|
+
PipelineDeploymentRequest: ResourceType.PIPELINE_DEPLOYMENT,
|
505
|
+
PipelineDeploymentResponse: ResourceType.PIPELINE_DEPLOYMENT,
|
506
|
+
PipelineRequest: ResourceType.PIPELINE,
|
507
|
+
PipelineResponse: ResourceType.PIPELINE,
|
508
|
+
PipelineRunRequest: ResourceType.PIPELINE_RUN,
|
439
509
|
PipelineRunResponse: ResourceType.PIPELINE_RUN,
|
510
|
+
RunMetadataRequest: ResourceType.RUN_METADATA,
|
511
|
+
RunTemplateRequest: ResourceType.RUN_TEMPLATE,
|
440
512
|
RunTemplateResponse: ResourceType.RUN_TEMPLATE,
|
513
|
+
SecretRequest: ResourceType.SECRET,
|
514
|
+
SecretResponse: ResourceType.SECRET,
|
515
|
+
ServiceAccountRequest: ResourceType.SERVICE_ACCOUNT,
|
516
|
+
ServiceAccountResponse: ResourceType.SERVICE_ACCOUNT,
|
517
|
+
ServiceConnectorRequest: ResourceType.SERVICE_CONNECTOR,
|
518
|
+
ServiceConnectorResponse: ResourceType.SERVICE_CONNECTOR,
|
519
|
+
ServiceRequest: ResourceType.SERVICE,
|
520
|
+
ServiceResponse: ResourceType.SERVICE,
|
521
|
+
StackRequest: ResourceType.STACK,
|
522
|
+
StackResponse: ResourceType.STACK,
|
523
|
+
TagRequest: ResourceType.TAG,
|
441
524
|
TagResponse: ResourceType.TAG,
|
525
|
+
TriggerRequest: ResourceType.TRIGGER,
|
442
526
|
TriggerResponse: ResourceType.TRIGGER,
|
527
|
+
TriggerExecutionRequest: ResourceType.TRIGGER_EXECUTION,
|
443
528
|
TriggerExecutionResponse: ResourceType.TRIGGER_EXECUTION,
|
444
|
-
|
445
|
-
|
529
|
+
ProjectResponse: ResourceType.PROJECT,
|
530
|
+
ProjectRequest: ResourceType.PROJECT,
|
531
|
+
# UserResponse: ResourceType.USER,
|
446
532
|
}
|
447
533
|
|
448
534
|
return mapping.get(type(model))
|
449
535
|
|
450
536
|
|
451
|
-
def is_owned_by_authenticated_user(model:
|
537
|
+
def is_owned_by_authenticated_user(model: AnyModel) -> bool:
|
452
538
|
"""Returns whether the currently authenticated user owns the model.
|
453
539
|
|
454
540
|
Args:
|
@@ -585,7 +671,7 @@ def get_schema_for_resource_type(
|
|
585
671
|
ResourceType.SERVICE: ServiceSchema,
|
586
672
|
ResourceType.TAG: TagSchema,
|
587
673
|
ResourceType.SERVICE_ACCOUNT: UserSchema,
|
588
|
-
# ResourceType.
|
674
|
+
# ResourceType.PROJECT: ProjectSchema,
|
589
675
|
ResourceType.PIPELINE_RUN: PipelineRunSchema,
|
590
676
|
ResourceType.PIPELINE_DEPLOYMENT: PipelineDeploymentSchema,
|
591
677
|
ResourceType.PIPELINE_BUILD: PipelineBuildSchema,
|
@@ -618,3 +704,42 @@ def update_resource_membership(
|
|
618
704
|
rbac().update_resource_membership(
|
619
705
|
user=user, resource=resource, actions=actions
|
620
706
|
)
|
707
|
+
|
708
|
+
|
709
|
+
def delete_model_resource(model: AnyModel) -> None:
|
710
|
+
"""Delete resource membership information for a model.
|
711
|
+
|
712
|
+
Args:
|
713
|
+
model: The model for which to delete the resource membership information.
|
714
|
+
"""
|
715
|
+
delete_model_resources(models=[model])
|
716
|
+
|
717
|
+
|
718
|
+
def delete_model_resources(models: List[AnyModel]) -> None:
|
719
|
+
"""Delete resource membership information for a list of models.
|
720
|
+
|
721
|
+
Args:
|
722
|
+
models: The models for which to delete the resource membership information.
|
723
|
+
"""
|
724
|
+
if not server_config().rbac_enabled:
|
725
|
+
return
|
726
|
+
|
727
|
+
resources = set()
|
728
|
+
for model in models:
|
729
|
+
if resource := get_resource_for_model(model):
|
730
|
+
resources.add(resource)
|
731
|
+
|
732
|
+
delete_resources(resources=list(resources))
|
733
|
+
|
734
|
+
|
735
|
+
def delete_resources(resources: List[Resource]) -> None:
|
736
|
+
"""Delete resource membership information for a list of resources.
|
737
|
+
|
738
|
+
Args:
|
739
|
+
resources: The resources for which to delete the resource membership
|
740
|
+
information.
|
741
|
+
"""
|
742
|
+
if not server_config().rbac_enabled:
|
743
|
+
return
|
744
|
+
|
745
|
+
rbac().delete_resources(resources=resources)
|
@@ -13,10 +13,10 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Cloud RBAC implementation."""
|
15
15
|
|
16
|
-
from typing import TYPE_CHECKING, Dict, List, Set, Tuple
|
16
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
|
17
17
|
|
18
18
|
from zenml.zen_server.cloud_utils import cloud_connection
|
19
|
-
from zenml.zen_server.rbac.models import Action, Resource
|
19
|
+
from zenml.zen_server.rbac.models import Action, Resource, ResourceType
|
20
20
|
from zenml.zen_server.rbac.rbac_interface import RBACInterface
|
21
21
|
from zenml.zen_server.utils import server_config
|
22
22
|
|
@@ -27,6 +27,7 @@ if TYPE_CHECKING:
|
|
27
27
|
PERMISSIONS_ENDPOINT = "/rbac/check_permissions"
|
28
28
|
ALLOWED_RESOURCE_IDS_ENDPOINT = "/rbac/allowed_resource_ids"
|
29
29
|
RESOURCE_MEMBERSHIP_ENDPOINT = "/rbac/resource_members"
|
30
|
+
RESOURCES_ENDPOINT = "/rbac/resources"
|
30
31
|
|
31
32
|
SERVER_SCOPE_IDENTIFIER = "server"
|
32
33
|
|
@@ -42,12 +43,7 @@ def _convert_to_cloud_resource(resource: Resource) -> str:
|
|
42
43
|
Returns:
|
43
44
|
The converted resource.
|
44
45
|
"""
|
45
|
-
|
46
|
-
|
47
|
-
if resource.id:
|
48
|
-
resource_string += f"/{resource.id}"
|
49
|
-
|
50
|
-
return resource_string
|
46
|
+
return f"{SERVER_ID}@{SERVER_SCOPE_IDENTIFIER}:{resource}"
|
51
47
|
|
52
48
|
|
53
49
|
def _convert_from_cloud_resource(cloud_resource: str) -> Resource:
|
@@ -62,16 +58,34 @@ def _convert_from_cloud_resource(cloud_resource: str) -> Resource:
|
|
62
58
|
Returns:
|
63
59
|
The converted resource.
|
64
60
|
"""
|
65
|
-
scope,
|
61
|
+
scope, project_resource_type_and_id = cloud_resource.split(":", maxsplit=1)
|
66
62
|
|
67
63
|
if scope != f"{SERVER_ID}@{SERVER_SCOPE_IDENTIFIER}":
|
68
64
|
raise ValueError("Invalid scope for server resource.")
|
69
65
|
|
66
|
+
project_id: Optional[str] = None
|
67
|
+
if ":" in project_resource_type_and_id:
|
68
|
+
(
|
69
|
+
project_id,
|
70
|
+
resource_type_and_id,
|
71
|
+
) = project_resource_type_and_id.split(":", maxsplit=1)
|
72
|
+
else:
|
73
|
+
project_id = None
|
74
|
+
resource_type_and_id = project_resource_type_and_id
|
75
|
+
|
76
|
+
resource_id: Optional[str] = None
|
70
77
|
if "/" in resource_type_and_id:
|
71
78
|
resource_type, resource_id = resource_type_and_id.split("/")
|
72
|
-
return Resource(type=resource_type, id=resource_id)
|
73
79
|
else:
|
74
|
-
|
80
|
+
resource_type = resource_type_and_id
|
81
|
+
|
82
|
+
if resource_type == ResourceType.PROJECT and project_id is not None:
|
83
|
+
# TODO: For now, we duplicate the project ID in the string
|
84
|
+
# representation when describing a project instance, because
|
85
|
+
# this is what is expected by the RBAC implementation.
|
86
|
+
project_id = None
|
87
|
+
|
88
|
+
return Resource(type=resource_type, id=resource_id, project_id=project_id)
|
75
89
|
|
76
90
|
|
77
91
|
class ZenMLCloudRBAC(RBACInterface):
|
@@ -184,3 +198,17 @@ class ZenMLCloudRBAC(RBACInterface):
|
|
184
198
|
"actions": [str(action) for action in actions],
|
185
199
|
}
|
186
200
|
self._connection.post(endpoint=RESOURCE_MEMBERSHIP_ENDPOINT, data=data)
|
201
|
+
|
202
|
+
def delete_resources(self, resources: List[Resource]) -> None:
|
203
|
+
"""Delete resource membership information for a list of resources.
|
204
|
+
|
205
|
+
Args:
|
206
|
+
resources: The resources for which to delete the resource membership
|
207
|
+
information.
|
208
|
+
"""
|
209
|
+
params = {
|
210
|
+
"resources": [
|
211
|
+
_convert_to_cloud_resource(resource) for resource in resources
|
212
|
+
],
|
213
|
+
}
|
214
|
+
self._connection.delete(endpoint=RESOURCES_ENDPOINT, params=params)
|
@@ -40,6 +40,7 @@ from zenml.zen_server.rbac.endpoint_utils import (
|
|
40
40
|
from zenml.zen_server.rbac.models import Action, ResourceType
|
41
41
|
from zenml.zen_server.rbac.utils import (
|
42
42
|
dehydrate_response_model,
|
43
|
+
delete_model_resource,
|
43
44
|
verify_permission_for_model,
|
44
45
|
)
|
45
46
|
from zenml.zen_server.utils import (
|
@@ -58,7 +59,6 @@ router = APIRouter(
|
|
58
59
|
|
59
60
|
@router.get(
|
60
61
|
"",
|
61
|
-
response_model=Page[ActionResponse],
|
62
62
|
responses={401: error_response, 404: error_response, 422: error_response},
|
63
63
|
)
|
64
64
|
@handle_exceptions
|
@@ -130,7 +130,6 @@ def list_actions(
|
|
130
130
|
|
131
131
|
@router.get(
|
132
132
|
"/{action_id}",
|
133
|
-
response_model=ActionResponse,
|
134
133
|
responses={401: error_response, 404: error_response, 422: error_response},
|
135
134
|
)
|
136
135
|
@handle_exceptions
|
@@ -178,7 +177,6 @@ def get_action(
|
|
178
177
|
|
179
178
|
@router.post(
|
180
179
|
"",
|
181
|
-
response_model=ActionResponse,
|
182
180
|
responses={401: error_response, 409: error_response, 422: error_response},
|
183
181
|
)
|
184
182
|
@handle_exceptions
|
@@ -219,14 +217,12 @@ def create_action(
|
|
219
217
|
|
220
218
|
return verify_permissions_and_create_entity(
|
221
219
|
request_model=action,
|
222
|
-
resource_type=ResourceType.ACTION,
|
223
220
|
create_method=action_handler.create_action,
|
224
221
|
)
|
225
222
|
|
226
223
|
|
227
224
|
@router.put(
|
228
225
|
"/{action_id}",
|
229
|
-
response_model=ActionResponse,
|
230
226
|
responses={401: error_response, 404: error_response, 422: error_response},
|
231
227
|
)
|
232
228
|
@handle_exceptions
|
@@ -322,3 +318,5 @@ def delete_action(
|
|
322
318
|
action=action,
|
323
319
|
force=force,
|
324
320
|
)
|
321
|
+
|
322
|
+
delete_model_resource(action)
|
@@ -50,7 +50,6 @@ artifact_router = APIRouter(
|
|
50
50
|
|
51
51
|
@artifact_router.get(
|
52
52
|
"",
|
53
|
-
response_model=Page[ArtifactResponse],
|
54
53
|
responses={401: error_response, 404: error_response, 422: error_response},
|
55
54
|
)
|
56
55
|
@handle_exceptions
|
@@ -82,7 +81,6 @@ def list_artifacts(
|
|
82
81
|
|
83
82
|
@artifact_router.post(
|
84
83
|
"",
|
85
|
-
response_model=ArtifactResponse,
|
86
84
|
responses={401: error_response, 409: error_response, 422: error_response},
|
87
85
|
)
|
88
86
|
@handle_exceptions
|
@@ -100,14 +98,12 @@ def create_artifact(
|
|
100
98
|
"""
|
101
99
|
return verify_permissions_and_create_entity(
|
102
100
|
request_model=artifact,
|
103
|
-
resource_type=ResourceType.ARTIFACT,
|
104
101
|
create_method=zen_store().create_artifact,
|
105
102
|
)
|
106
103
|
|
107
104
|
|
108
105
|
@artifact_router.get(
|
109
106
|
"/{artifact_id}",
|
110
|
-
response_model=ArtifactResponse,
|
111
107
|
responses={401: error_response, 404: error_response, 422: error_response},
|
112
108
|
)
|
113
109
|
@handle_exceptions
|
@@ -135,7 +131,6 @@ def get_artifact(
|
|
135
131
|
|
136
132
|
@artifact_router.put(
|
137
133
|
"/{artifact_id}",
|
138
|
-
response_model=ArtifactResponse,
|
139
134
|
responses={401: error_response, 404: error_response, 422: error_response},
|
140
135
|
)
|
141
136
|
@handle_exceptions
|
@@ -13,7 +13,7 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Endpoint definitions for artifact versions."""
|
15
15
|
|
16
|
-
from typing import List
|
16
|
+
from typing import List, Union
|
17
17
|
from uuid import UUID
|
18
18
|
|
19
19
|
from fastapi import APIRouter, Depends, Security
|
@@ -46,6 +46,7 @@ from zenml.zen_server.rbac.utils import (
|
|
46
46
|
from zenml.zen_server.utils import (
|
47
47
|
handle_exceptions,
|
48
48
|
make_dependable,
|
49
|
+
set_filter_project_scope,
|
49
50
|
zen_store,
|
50
51
|
)
|
51
52
|
|
@@ -58,7 +59,6 @@ artifact_version_router = APIRouter(
|
|
58
59
|
|
59
60
|
@artifact_version_router.get(
|
60
61
|
"",
|
61
|
-
response_model=Page[ArtifactVersionResponse],
|
62
62
|
responses={401: error_response, 404: error_response, 422: error_response},
|
63
63
|
)
|
64
64
|
@handle_exceptions
|
@@ -81,8 +81,14 @@ def list_artifact_versions(
|
|
81
81
|
Returns:
|
82
82
|
The artifact versions according to query filters.
|
83
83
|
"""
|
84
|
+
# A project scoped request must always be scoped to a specific
|
85
|
+
# project. This is required for the RBAC check to work.
|
86
|
+
set_filter_project_scope(artifact_version_filter_model)
|
87
|
+
assert isinstance(artifact_version_filter_model.project, UUID)
|
88
|
+
|
84
89
|
allowed_artifact_ids = get_allowed_resource_ids(
|
85
|
-
resource_type=ResourceType.ARTIFACT
|
90
|
+
resource_type=ResourceType.ARTIFACT,
|
91
|
+
project_id=artifact_version_filter_model.project,
|
86
92
|
)
|
87
93
|
artifact_version_filter_model.configure_rbac(
|
88
94
|
authenticated_user_id=auth_context.user.id,
|
@@ -97,7 +103,6 @@ def list_artifact_versions(
|
|
97
103
|
|
98
104
|
@artifact_version_router.post(
|
99
105
|
"",
|
100
|
-
response_model=ArtifactVersionResponse,
|
101
106
|
responses={401: error_response, 409: error_response, 422: error_response},
|
102
107
|
)
|
103
108
|
@handle_exceptions
|
@@ -115,7 +120,6 @@ def create_artifact_version(
|
|
115
120
|
"""
|
116
121
|
return verify_permissions_and_create_entity(
|
117
122
|
request_model=artifact_version,
|
118
|
-
resource_type=ResourceType.ARTIFACT_VERSION,
|
119
123
|
create_method=zen_store().create_artifact_version,
|
120
124
|
)
|
121
125
|
|
@@ -139,14 +143,12 @@ def batch_create_artifact_version(
|
|
139
143
|
"""
|
140
144
|
return verify_permissions_and_batch_create_entity(
|
141
145
|
batch=artifact_versions,
|
142
|
-
resource_type=ResourceType.ARTIFACT_VERSION,
|
143
146
|
create_method=zen_store().batch_create_artifact_versions,
|
144
147
|
)
|
145
148
|
|
146
149
|
|
147
150
|
@artifact_version_router.get(
|
148
151
|
"/{artifact_version_id}",
|
149
|
-
response_model=ArtifactVersionResponse,
|
150
152
|
responses={401: error_response, 404: error_response, 422: error_response},
|
151
153
|
)
|
152
154
|
@handle_exceptions
|
@@ -174,7 +176,6 @@ def get_artifact_version(
|
|
174
176
|
|
175
177
|
@artifact_version_router.put(
|
176
178
|
"/{artifact_version_id}",
|
177
|
-
response_model=ArtifactVersionResponse,
|
178
179
|
responses={401: error_response, 404: error_response, 422: error_response},
|
179
180
|
)
|
180
181
|
@handle_exceptions
|
@@ -227,24 +228,29 @@ def delete_artifact_version(
|
|
227
228
|
)
|
228
229
|
@handle_exceptions
|
229
230
|
def prune_artifact_versions(
|
231
|
+
project_name_or_id: Union[str, UUID],
|
230
232
|
only_versions: bool = True,
|
231
233
|
_: AuthContext = Security(authorize),
|
232
234
|
) -> None:
|
233
235
|
"""Prunes unused artifact versions and their artifacts.
|
234
236
|
|
235
237
|
Args:
|
238
|
+
project_name_or_id: The project name or ID to prune artifact
|
239
|
+
versions for.
|
236
240
|
only_versions: Only delete artifact versions, keeping artifacts
|
237
241
|
"""
|
242
|
+
project_id = zen_store().get_project(project_name_or_id).id
|
243
|
+
|
238
244
|
verify_permissions_and_prune_entities(
|
239
245
|
resource_type=ResourceType.ARTIFACT_VERSION,
|
240
246
|
prune_method=zen_store().prune_artifact_versions,
|
241
247
|
only_versions=only_versions,
|
248
|
+
project_id=project_id,
|
242
249
|
)
|
243
250
|
|
244
251
|
|
245
252
|
@artifact_version_router.get(
|
246
253
|
"/{artifact_version_id}" + VISUALIZE,
|
247
|
-
response_model=LoadedVisualization,
|
248
254
|
responses={401: error_response, 404: error_response, 422: error_response},
|
249
255
|
)
|
250
256
|
@handle_exceptions
|