zenml-nightly 0.75.0.dev20250313__py3-none-any.whl → 0.75.0.dev20250315__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.
Files changed (145) hide show
  1. zenml/VERSION +1 -1
  2. zenml/analytics/context.py +4 -4
  3. zenml/analytics/enums.py +2 -2
  4. zenml/artifacts/utils.py +2 -2
  5. zenml/cli/__init__.py +8 -9
  6. zenml/cli/base.py +2 -2
  7. zenml/cli/code_repository.py +1 -1
  8. zenml/cli/login.py +21 -18
  9. zenml/cli/pipeline.py +3 -3
  10. zenml/cli/project.py +172 -0
  11. zenml/cli/server.py +5 -5
  12. zenml/cli/service_accounts.py +0 -1
  13. zenml/cli/service_connectors.py +15 -16
  14. zenml/cli/stack.py +0 -2
  15. zenml/cli/stack_components.py +2 -2
  16. zenml/cli/utils.py +3 -3
  17. zenml/client.py +352 -341
  18. zenml/config/global_config.py +41 -43
  19. zenml/config/server_config.py +9 -9
  20. zenml/constants.py +5 -3
  21. zenml/event_hub/event_hub.py +1 -1
  22. zenml/integrations/gcp/__init__.py +1 -0
  23. zenml/integrations/gcp/flavors/vertex_orchestrator_flavor.py +5 -0
  24. zenml/integrations/gcp/flavors/vertex_step_operator_flavor.py +5 -28
  25. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +125 -78
  26. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +7 -6
  27. zenml/integrations/gcp/vertex_custom_job_parameters.py +50 -0
  28. zenml/integrations/mlflow/steps/mlflow_registry.py +3 -3
  29. zenml/integrations/wandb/__init__.py +1 -1
  30. zenml/integrations/wandb/experiment_trackers/wandb_experiment_tracker.py +29 -9
  31. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +2 -0
  32. zenml/login/credentials.py +26 -27
  33. zenml/login/credentials_store.py +5 -5
  34. zenml/login/pro/client.py +9 -9
  35. zenml/login/pro/utils.py +8 -8
  36. zenml/login/pro/{tenant → workspace}/__init__.py +1 -1
  37. zenml/login/pro/{tenant → workspace}/client.py +25 -25
  38. zenml/login/pro/{tenant → workspace}/models.py +27 -28
  39. zenml/model/model.py +2 -2
  40. zenml/model_registries/base_model_registry.py +1 -1
  41. zenml/models/__init__.py +29 -29
  42. zenml/models/v2/base/filter.py +1 -1
  43. zenml/models/v2/base/scoped.py +49 -53
  44. zenml/models/v2/core/action.py +12 -12
  45. zenml/models/v2/core/artifact.py +15 -15
  46. zenml/models/v2/core/artifact_version.py +15 -15
  47. zenml/models/v2/core/code_repository.py +12 -12
  48. zenml/models/v2/core/event_source.py +12 -12
  49. zenml/models/v2/core/model.py +26 -18
  50. zenml/models/v2/core/model_version.py +15 -15
  51. zenml/models/v2/core/pipeline.py +15 -15
  52. zenml/models/v2/core/pipeline_build.py +14 -14
  53. zenml/models/v2/core/pipeline_deployment.py +12 -14
  54. zenml/models/v2/core/pipeline_run.py +16 -16
  55. zenml/models/v2/core/project.py +203 -0
  56. zenml/models/v2/core/run_metadata.py +2 -2
  57. zenml/models/v2/core/run_template.py +15 -15
  58. zenml/models/v2/core/schedule.py +12 -12
  59. zenml/models/v2/core/secret.py +1 -1
  60. zenml/models/v2/core/service.py +14 -14
  61. zenml/models/v2/core/step_run.py +13 -13
  62. zenml/models/v2/core/tag.py +96 -3
  63. zenml/models/v2/core/trigger.py +13 -13
  64. zenml/models/v2/core/trigger_execution.py +2 -2
  65. zenml/models/v2/core/user.py +0 -17
  66. zenml/models/v2/misc/server_models.py +6 -6
  67. zenml/models/v2/misc/statistics.py +4 -4
  68. zenml/orchestrators/cache_utils.py +7 -7
  69. zenml/orchestrators/input_utils.py +1 -1
  70. zenml/orchestrators/step_launcher.py +1 -1
  71. zenml/orchestrators/step_run_utils.py +3 -3
  72. zenml/orchestrators/utils.py +4 -4
  73. zenml/pipelines/build_utils.py +2 -2
  74. zenml/pipelines/pipeline_definition.py +5 -5
  75. zenml/pipelines/run_utils.py +1 -1
  76. zenml/service_connectors/service_connector.py +0 -3
  77. zenml/service_connectors/service_connector_utils.py +0 -1
  78. zenml/stack/stack.py +0 -1
  79. zenml/steps/base_step.py +10 -2
  80. zenml/utils/dashboard_utils.py +1 -1
  81. zenml/utils/tag_utils.py +0 -12
  82. zenml/zen_server/cloud_utils.py +3 -3
  83. zenml/zen_server/feature_gate/endpoint_utils.py +1 -1
  84. zenml/zen_server/feature_gate/zenml_cloud_feature_gate.py +1 -1
  85. zenml/zen_server/rbac/endpoint_utils.py +17 -17
  86. zenml/zen_server/rbac/models.py +47 -22
  87. zenml/zen_server/rbac/rbac_sql_zen_store.py +3 -3
  88. zenml/zen_server/rbac/utils.py +23 -25
  89. zenml/zen_server/rbac/zenml_cloud_rbac.py +7 -74
  90. zenml/zen_server/routers/artifact_version_endpoints.py +10 -10
  91. zenml/zen_server/routers/auth_endpoints.py +6 -6
  92. zenml/zen_server/routers/code_repositories_endpoints.py +12 -14
  93. zenml/zen_server/routers/model_versions_endpoints.py +13 -15
  94. zenml/zen_server/routers/models_endpoints.py +7 -9
  95. zenml/zen_server/routers/pipeline_builds_endpoints.py +14 -16
  96. zenml/zen_server/routers/pipeline_deployments_endpoints.py +13 -15
  97. zenml/zen_server/routers/pipelines_endpoints.py +16 -18
  98. zenml/zen_server/routers/{workspaces_endpoints.py → projects_endpoints.py} +111 -68
  99. zenml/zen_server/routers/run_metadata_endpoints.py +7 -9
  100. zenml/zen_server/routers/run_templates_endpoints.py +15 -17
  101. zenml/zen_server/routers/runs_endpoints.py +12 -14
  102. zenml/zen_server/routers/schedule_endpoints.py +12 -14
  103. zenml/zen_server/routers/secrets_endpoints.py +1 -3
  104. zenml/zen_server/routers/server_endpoints.py +7 -7
  105. zenml/zen_server/routers/service_connectors_endpoints.py +11 -13
  106. zenml/zen_server/routers/service_endpoints.py +7 -9
  107. zenml/zen_server/routers/stack_components_endpoints.py +9 -11
  108. zenml/zen_server/routers/stacks_endpoints.py +9 -11
  109. zenml/zen_server/routers/steps_endpoints.py +6 -6
  110. zenml/zen_server/routers/users_endpoints.py +5 -43
  111. zenml/zen_server/template_execution/utils.py +4 -4
  112. zenml/zen_server/utils.py +10 -10
  113. zenml/zen_server/zen_server_api.py +6 -5
  114. zenml/zen_stores/base_zen_store.py +38 -42
  115. zenml/zen_stores/migrations/versions/12eff0206201_rename_workspace_to_project.py +768 -0
  116. zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +3 -3
  117. zenml/zen_stores/migrations/versions/cbc6acd71f92_add_workspace_display_name.py +58 -0
  118. zenml/zen_stores/rest_zen_store.py +55 -63
  119. zenml/zen_stores/schemas/__init__.py +2 -2
  120. zenml/zen_stores/schemas/action_schemas.py +9 -9
  121. zenml/zen_stores/schemas/artifact_schemas.py +15 -17
  122. zenml/zen_stores/schemas/code_repository_schemas.py +16 -18
  123. zenml/zen_stores/schemas/event_source_schemas.py +9 -9
  124. zenml/zen_stores/schemas/model_schemas.py +15 -17
  125. zenml/zen_stores/schemas/pipeline_build_schemas.py +7 -7
  126. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +7 -7
  127. zenml/zen_stores/schemas/pipeline_run_schemas.py +9 -9
  128. zenml/zen_stores/schemas/pipeline_schemas.py +9 -9
  129. zenml/zen_stores/schemas/{workspace_schemas.py → project_schemas.py} +47 -41
  130. zenml/zen_stores/schemas/run_metadata_schemas.py +5 -5
  131. zenml/zen_stores/schemas/run_template_schemas.py +9 -9
  132. zenml/zen_stores/schemas/schedule_schema.py +9 -9
  133. zenml/zen_stores/schemas/service_schemas.py +7 -7
  134. zenml/zen_stores/schemas/step_run_schemas.py +7 -7
  135. zenml/zen_stores/schemas/trigger_schemas.py +9 -9
  136. zenml/zen_stores/schemas/user_schemas.py +0 -12
  137. zenml/zen_stores/sql_zen_store.py +318 -275
  138. zenml/zen_stores/zen_store_interface.py +56 -70
  139. {zenml_nightly-0.75.0.dev20250313.dist-info → zenml_nightly-0.75.0.dev20250315.dist-info}/METADATA +1 -1
  140. {zenml_nightly-0.75.0.dev20250313.dist-info → zenml_nightly-0.75.0.dev20250315.dist-info}/RECORD +143 -140
  141. zenml/cli/workspace.py +0 -160
  142. zenml/models/v2/core/workspace.py +0 -131
  143. {zenml_nightly-0.75.0.dev20250313.dist-info → zenml_nightly-0.75.0.dev20250315.dist-info}/LICENSE +0 -0
  144. {zenml_nightly-0.75.0.dev20250313.dist-info → zenml_nightly-0.75.0.dev20250315.dist-info}/WHEEL +0 -0
  145. {zenml_nightly-0.75.0.dev20250313.dist-info → zenml_nightly-0.75.0.dev20250315.dist-info}/entry_points.txt +0 -0
@@ -22,8 +22,8 @@ from zenml.models import (
22
22
  BaseRequest,
23
23
  BaseUpdate,
24
24
  Page,
25
+ ProjectScopedFilter,
25
26
  UserScopedRequest,
26
- WorkspaceScopedFilter,
27
27
  )
28
28
  from zenml.zen_server.auth import get_auth_context
29
29
  from zenml.zen_server.feature_gate.endpoint_utils import (
@@ -42,7 +42,7 @@ from zenml.zen_server.rbac.utils import (
42
42
  verify_permission,
43
43
  verify_permission_for_model,
44
44
  )
45
- from zenml.zen_server.utils import server_config, set_filter_workspace_scope
45
+ from zenml.zen_server.utils import server_config, set_filter_project_scope
46
46
 
47
47
  AnyRequest = TypeVar("AnyRequest", bound=BaseRequest)
48
48
  AnyResponse = TypeVar("AnyResponse", bound=BaseIdentifiedResponse) # type: ignore[type-arg]
@@ -232,27 +232,27 @@ def verify_permissions_and_list_entities(
232
232
  A page of entity models.
233
233
 
234
234
  Raises:
235
- ValueError: If the filter's workspace scope is not set or is not a UUID.
235
+ ValueError: If the filter's project scope is not set or is not a UUID.
236
236
  """
237
237
  auth_context = get_auth_context()
238
238
  assert auth_context
239
239
 
240
- workspace_id: Optional[UUID] = None
241
- if isinstance(filter_model, WorkspaceScopedFilter):
242
- # A workspace scoped filter must always be scoped to a specific
243
- # workspace. This is required for the RBAC check to work.
244
- set_filter_workspace_scope(filter_model)
245
- if not filter_model.workspace or not isinstance(
246
- filter_model.workspace, UUID
240
+ project_id: Optional[UUID] = None
241
+ if isinstance(filter_model, ProjectScopedFilter):
242
+ # A project scoped filter must always be scoped to a specific
243
+ # project. This is required for the RBAC check to work.
244
+ set_filter_project_scope(filter_model)
245
+ if not filter_model.project or not isinstance(
246
+ filter_model.project, UUID
247
247
  ):
248
248
  raise ValueError(
249
- "Workspace scope must be a UUID, got "
250
- f"{type(filter_model.workspace)}."
249
+ "Project scope must be a UUID, got "
250
+ f"{type(filter_model.project)}."
251
251
  )
252
- workspace_id = filter_model.workspace
252
+ project_id = filter_model.project
253
253
 
254
254
  allowed_ids = get_allowed_resource_ids(
255
- resource_type=resource_type, workspace_id=workspace_id
255
+ resource_type=resource_type, project_id=project_id
256
256
  )
257
257
  filter_model.configure_rbac(
258
258
  authenticated_user_id=auth_context.user.id, id=allowed_ids
@@ -318,7 +318,7 @@ def verify_permissions_and_delete_entity(
318
318
  def verify_permissions_and_prune_entities(
319
319
  resource_type: ResourceType,
320
320
  prune_method: Callable[..., None],
321
- workspace_id: Optional[UUID] = None,
321
+ project_id: Optional[UUID] = None,
322
322
  **kwargs: Any,
323
323
  ) -> None:
324
324
  """Verify permissions and prune entities of certain type.
@@ -326,12 +326,12 @@ def verify_permissions_and_prune_entities(
326
326
  Args:
327
327
  resource_type: The resource type of the entities to prune.
328
328
  prune_method: The method to prune the entities.
329
- workspace_id: The workspace ID to prune the entities for.
329
+ project_id: The project ID to prune the entities for.
330
330
  kwargs: Keyword arguments to pass to the prune method.
331
331
  """
332
332
  verify_permission(
333
333
  resource_type=resource_type,
334
334
  action=Action.PRUNE,
335
- workspace_id=workspace_id,
335
+ project_id=project_id,
336
336
  )
337
337
  prune_method(**kwargs)
@@ -73,15 +73,15 @@ class ResourceType(StrEnum):
73
73
  TAG = "tag"
74
74
  TRIGGER = "trigger"
75
75
  TRIGGER_EXECUTION = "trigger_execution"
76
- WORKSPACE = "workspace"
76
+ PROJECT = "project"
77
77
  # Deactivated for now
78
78
  # USER = "user"
79
79
 
80
- def is_workspace_scoped(self) -> bool:
81
- """Check if a resource type is workspace scoped.
80
+ def is_project_scoped(self) -> bool:
81
+ """Check if a resource type is project scoped.
82
82
 
83
83
  Returns:
84
- Whether the resource type is workspace scoped.
84
+ Whether the resource type is project scoped.
85
85
  """
86
86
  return self not in [
87
87
  self.FLAVOR,
@@ -91,7 +91,7 @@ class ResourceType(StrEnum):
91
91
  self.STACK_COMPONENT,
92
92
  self.TAG,
93
93
  self.SERVICE_ACCOUNT,
94
- self.WORKSPACE,
94
+ self.PROJECT,
95
95
  # Deactivated for now
96
96
  # self.USER,
97
97
  ]
@@ -102,7 +102,7 @@ class Resource(BaseModel):
102
102
 
103
103
  type: str
104
104
  id: Optional[UUID] = None
105
- workspace_id: Optional[UUID] = None
105
+ project_id: Optional[UUID] = None
106
106
 
107
107
  def __str__(self) -> str:
108
108
  """Convert to a string.
@@ -110,15 +110,10 @@ class Resource(BaseModel):
110
110
  Returns:
111
111
  Resource string representation.
112
112
  """
113
- workspace_id = self.workspace_id
114
- if self.type == ResourceType.WORKSPACE and self.id:
115
- # TODO: For now, we duplicate the workspace ID in the string
116
- # representation when describing a workspace instance, because
117
- # this is what is expected by the RBAC implementation.
118
- workspace_id = self.id
119
-
120
- if workspace_id:
121
- representation = f"{workspace_id}:"
113
+ project_id = self.project_id
114
+
115
+ if project_id:
116
+ representation = f"{project_id}:"
122
117
  else:
123
118
  representation = ""
124
119
  representation += self.type
@@ -127,12 +122,42 @@ class Resource(BaseModel):
127
122
 
128
123
  return representation
129
124
 
125
+ @classmethod
126
+ def parse(cls, resource: str) -> "Resource":
127
+ """Parse an RBAC resource string into a Resource object.
128
+
129
+ Args:
130
+ resource: The resource to convert.
131
+
132
+ Returns:
133
+ The converted resource.
134
+ """
135
+ project_id: Optional[str] = None
136
+ if ":" in resource:
137
+ (
138
+ project_id,
139
+ resource_type_and_id,
140
+ ) = resource.split(":", maxsplit=1)
141
+ else:
142
+ project_id = None
143
+ resource_type_and_id = resource
144
+
145
+ resource_id: Optional[str] = None
146
+ if "/" in resource_type_and_id:
147
+ resource_type, resource_id = resource_type_and_id.split("/")
148
+ else:
149
+ resource_type = resource_type_and_id
150
+
151
+ return Resource(
152
+ type=resource_type, id=resource_id, project_id=project_id
153
+ )
154
+
130
155
  @model_validator(mode="after")
131
- def validate_workspace_id(self) -> "Resource":
132
- """Validate that workspace_id is set in combination with workspace-scoped resource types.
156
+ def validate_project_id(self) -> "Resource":
157
+ """Validate that project_id is set in combination with project-scoped resource types.
133
158
 
134
159
  Raises:
135
- ValueError: If workspace_id is not set for a workspace-scoped
160
+ ValueError: If project_id is not set for a project-scoped
136
161
  resource or set for an unscoped resource.
137
162
 
138
163
  Returns:
@@ -140,15 +165,15 @@ class Resource(BaseModel):
140
165
  """
141
166
  resource_type = ResourceType(self.type)
142
167
 
143
- if resource_type.is_workspace_scoped() and not self.workspace_id:
168
+ if resource_type.is_project_scoped() and not self.project_id:
144
169
  raise ValueError(
145
- "workspace_id must be set for workspace-scoped resource type "
170
+ "project_id must be set for project-scoped resource type "
146
171
  f"'{self.type}'"
147
172
  )
148
173
 
149
- if not resource_type.is_workspace_scoped() and self.workspace_id:
174
+ if not resource_type.is_project_scoped() and self.project_id:
150
175
  raise ValueError(
151
- "workspace_id must not be set for global resource type "
176
+ "project_id must not be set for global resource type "
152
177
  f"'{self.type}'"
153
178
  )
154
179
 
@@ -65,7 +65,7 @@ class RBACSqlZenStore(SqlZenStore):
65
65
  verify_permission(
66
66
  resource_type=ResourceType.MODEL,
67
67
  action=Action.CREATE,
68
- workspace_id=model_request.workspace,
68
+ project_id=model_request.project,
69
69
  )
70
70
  check_entitlement(resource_type=ResourceType.MODEL)
71
71
  except Exception as e:
@@ -80,7 +80,7 @@ class RBACSqlZenStore(SqlZenStore):
80
80
  try:
81
81
  model_response = self.get_model_by_name_or_id(
82
82
  model_name_or_id=model_request.name,
83
- workspace=model_request.workspace,
83
+ project=model_request.project,
84
84
  )
85
85
  created = False
86
86
  except KeyError:
@@ -152,7 +152,7 @@ class RBACSqlZenStore(SqlZenStore):
152
152
  verify_permission(
153
153
  resource_type=ResourceType.MODEL_VERSION,
154
154
  action=Action.CREATE,
155
- workspace_id=model_version_request.workspace,
155
+ project_id=model_version_request.project,
156
156
  )
157
157
  except Exception as e:
158
158
  allow_creation = False
@@ -32,10 +32,10 @@ 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
- WorkspaceScopedRequest,
38
- WorkspaceScopedResponse,
39
39
  )
40
40
  from zenml.zen_server.auth import get_auth_context
41
41
  from zenml.zen_server.rbac.models import Action, Resource, ResourceType
@@ -300,7 +300,7 @@ def verify_permission(
300
300
  resource_type: str,
301
301
  action: Action,
302
302
  resource_id: Optional[UUID] = None,
303
- workspace_id: Optional[UUID] = None,
303
+ project_id: Optional[UUID] = None,
304
304
  ) -> None:
305
305
  """Verifies if a user has permission to perform an action on a resource.
306
306
 
@@ -309,11 +309,11 @@ def verify_permission(
309
309
  action on.
310
310
  action: The action the user wants to perform.
311
311
  resource_id: ID of the resource the user wants to perform the action on.
312
- workspace_id: ID of the workspace the user wants to perform the action
313
- on. Only used for workspace scoped resources.
312
+ project_id: ID of the project the user wants to perform the action
313
+ on. Only used for project scoped resources.
314
314
  """
315
315
  resource = Resource(
316
- type=resource_type, id=resource_id, workspace_id=workspace_id
316
+ type=resource_type, id=resource_id, project_id=project_id
317
317
  )
318
318
  batch_verify_permissions(resources={resource}, action=action)
319
319
 
@@ -321,15 +321,15 @@ def verify_permission(
321
321
  def get_allowed_resource_ids(
322
322
  resource_type: str,
323
323
  action: Action = Action.READ,
324
- workspace_id: Optional[UUID] = None,
324
+ project_id: Optional[UUID] = None,
325
325
  ) -> Optional[Set[UUID]]:
326
326
  """Get all resource IDs of a resource type that a user can access.
327
327
 
328
328
  Args:
329
329
  resource_type: The resource type.
330
330
  action: The action the user wants to perform on the resource.
331
- workspace_id: Optional workspace ID to filter the resources by.
332
- Required for workspace scoped resources.
331
+ project_id: Optional project ID to filter the resources by.
332
+ Required for project scoped resources.
333
333
 
334
334
  Returns:
335
335
  A list of resource IDs or `None` if the user has full access to the
@@ -346,7 +346,7 @@ def get_allowed_resource_ids(
346
346
  allowed_ids,
347
347
  ) = rbac().list_allowed_resource_ids(
348
348
  user=auth_context.user,
349
- resource=Resource(type=resource_type, workspace_id=workspace_id),
349
+ resource=Resource(type=resource_type, project_id=project_id),
350
350
  action=action,
351
351
  )
352
352
 
@@ -371,21 +371,19 @@ def get_resource_for_model(model: AnyModel) -> Optional[Resource]:
371
371
  # This model is not tied to any RBAC resource type
372
372
  return None
373
373
 
374
- workspace_id: Optional[UUID] = None
375
- if isinstance(model, WorkspaceScopedResponse):
376
- # A workspace scoped response is always scoped to a specific workspace
377
- workspace_id = model.workspace.id
378
- elif isinstance(model, WorkspaceScopedRequest):
379
- # A workspace scoped request is always scoped to a specific workspace
380
- workspace_id = model.workspace
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
381
 
382
382
  resource_id: Optional[UUID] = None
383
383
  if isinstance(model, BaseIdentifiedResponse):
384
384
  resource_id = model.id
385
385
 
386
- return Resource(
387
- type=resource_type, id=resource_id, workspace_id=workspace_id
388
- )
386
+ return Resource(type=resource_type, id=resource_id, project_id=project_id)
389
387
 
390
388
 
391
389
  def get_surrogate_permission_model_for_model(
@@ -456,6 +454,8 @@ def get_resource_type_for_model(
456
454
  PipelineResponse,
457
455
  PipelineRunRequest,
458
456
  PipelineRunResponse,
457
+ ProjectRequest,
458
+ ProjectResponse,
459
459
  RunMetadataRequest,
460
460
  RunTemplateRequest,
461
461
  RunTemplateResponse,
@@ -475,8 +475,6 @@ def get_resource_type_for_model(
475
475
  TriggerExecutionResponse,
476
476
  TriggerRequest,
477
477
  TriggerResponse,
478
- WorkspaceRequest,
479
- WorkspaceResponse,
480
478
  )
481
479
 
482
480
  mapping: Dict[
@@ -528,8 +526,8 @@ def get_resource_type_for_model(
528
526
  TriggerResponse: ResourceType.TRIGGER,
529
527
  TriggerExecutionRequest: ResourceType.TRIGGER_EXECUTION,
530
528
  TriggerExecutionResponse: ResourceType.TRIGGER_EXECUTION,
531
- WorkspaceResponse: ResourceType.WORKSPACE,
532
- WorkspaceRequest: ResourceType.WORKSPACE,
529
+ ProjectResponse: ResourceType.PROJECT,
530
+ ProjectRequest: ResourceType.PROJECT,
533
531
  # UserResponse: ResourceType.USER,
534
532
  }
535
533
 
@@ -673,7 +671,7 @@ def get_schema_for_resource_type(
673
671
  ResourceType.SERVICE: ServiceSchema,
674
672
  ResourceType.TAG: TagSchema,
675
673
  ResourceType.SERVICE_ACCOUNT: UserSchema,
676
- # ResourceType.WORKSPACE: WorkspaceSchema,
674
+ # ResourceType.PROJECT: ProjectSchema,
677
675
  ResourceType.PIPELINE_RUN: PipelineRunSchema,
678
676
  ResourceType.PIPELINE_DEPLOYMENT: PipelineDeploymentSchema,
679
677
  ResourceType.PIPELINE_BUILD: PipelineBuildSchema,
@@ -13,12 +13,11 @@
13
13
  # permissions and limitations under the License.
14
14
  """Cloud RBAC implementation."""
15
15
 
16
- from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
16
+ from typing import TYPE_CHECKING, Dict, List, 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, ResourceType
19
+ from zenml.zen_server.rbac.models import Action, Resource
20
20
  from zenml.zen_server.rbac.rbac_interface import RBACInterface
21
- from zenml.zen_server.utils import server_config
22
21
 
23
22
  if TYPE_CHECKING:
24
23
  from zenml.models import UserResponse
@@ -29,68 +28,6 @@ ALLOWED_RESOURCE_IDS_ENDPOINT = "/rbac/allowed_resource_ids"
29
28
  RESOURCE_MEMBERSHIP_ENDPOINT = "/rbac/resource_members"
30
29
  RESOURCES_ENDPOINT = "/rbac/resources"
31
30
 
32
- SERVER_SCOPE_IDENTIFIER = "server"
33
-
34
- SERVER_ID = server_config().external_server_id
35
-
36
-
37
- def _convert_to_cloud_resource(resource: Resource) -> str:
38
- """Convert a resource to a ZenML Pro Management Plane resource.
39
-
40
- Args:
41
- resource: The resource to convert.
42
-
43
- Returns:
44
- The converted resource.
45
- """
46
- return f"{SERVER_ID}@{SERVER_SCOPE_IDENTIFIER}:{resource}"
47
-
48
-
49
- def _convert_from_cloud_resource(cloud_resource: str) -> Resource:
50
- """Convert a cloud resource to a ZenML server resource.
51
-
52
- Args:
53
- cloud_resource: The cloud resource to convert.
54
-
55
- Raises:
56
- ValueError: If the cloud resource is invalid for this server.
57
-
58
- Returns:
59
- The converted resource.
60
- """
61
- scope, workspace_resource_type_and_id = cloud_resource.split(
62
- ":", maxsplit=1
63
- )
64
-
65
- if scope != f"{SERVER_ID}@{SERVER_SCOPE_IDENTIFIER}":
66
- raise ValueError("Invalid scope for server resource.")
67
-
68
- workspace_id: Optional[str] = None
69
- if ":" in workspace_resource_type_and_id:
70
- (
71
- workspace_id,
72
- resource_type_and_id,
73
- ) = workspace_resource_type_and_id.split(":", maxsplit=1)
74
- else:
75
- workspace_id = None
76
- resource_type_and_id = workspace_resource_type_and_id
77
-
78
- resource_id: Optional[str] = None
79
- if "/" in resource_type_and_id:
80
- resource_type, resource_id = resource_type_and_id.split("/")
81
- else:
82
- resource_type = resource_type_and_id
83
-
84
- if resource_type == ResourceType.WORKSPACE and workspace_id is not None:
85
- # TODO: For now, we duplicate the workspace ID in the string
86
- # representation when describing a workspace instance, because
87
- # this is what is expected by the RBAC implementation.
88
- workspace_id = None
89
-
90
- return Resource(
91
- type=resource_type, id=resource_id, workspace_id=workspace_id
92
- )
93
-
94
31
 
95
32
  class ZenMLCloudRBAC(RBACInterface):
96
33
  """RBAC implementation that uses the ZenML Pro Management Plane as a backend."""
@@ -127,9 +64,7 @@ class ZenMLCloudRBAC(RBACInterface):
127
64
 
128
65
  params = {
129
66
  "user_id": str(user.external_user_id),
130
- "resources": [
131
- _convert_to_cloud_resource(resource) for resource in resources
132
- ],
67
+ "resources": resources,
133
68
  "action": str(action),
134
69
  }
135
70
  response = self._connection.get(
@@ -138,7 +73,7 @@ class ZenMLCloudRBAC(RBACInterface):
138
73
  value = response.json()
139
74
 
140
75
  assert isinstance(value, dict)
141
- return {_convert_from_cloud_resource(k): v for k, v in value.items()}
76
+ return {Resource.parse(k): v for k, v in value.items()}
142
77
 
143
78
  def list_allowed_resource_ids(
144
79
  self, user: "UserResponse", resource: Resource, action: Action
@@ -168,7 +103,7 @@ class ZenMLCloudRBAC(RBACInterface):
168
103
  assert user.external_user_id
169
104
  params = {
170
105
  "user_id": str(user.external_user_id),
171
- "resource": _convert_to_cloud_resource(resource),
106
+ "resource": str(resource),
172
107
  "action": str(action),
173
108
  }
174
109
  response = self._connection.get(
@@ -198,7 +133,7 @@ class ZenMLCloudRBAC(RBACInterface):
198
133
 
199
134
  data = {
200
135
  "user_id": str(user.external_user_id),
201
- "resource": _convert_to_cloud_resource(resource),
136
+ "resource": str(resource),
202
137
  "actions": [str(action) for action in actions],
203
138
  }
204
139
  self._connection.post(endpoint=RESOURCE_MEMBERSHIP_ENDPOINT, data=data)
@@ -211,8 +146,6 @@ class ZenMLCloudRBAC(RBACInterface):
211
146
  information.
212
147
  """
213
148
  params = {
214
- "resources": [
215
- _convert_to_cloud_resource(resource) for resource in resources
216
- ],
149
+ "resources": [str(resource) for resource in resources],
217
150
  }
218
151
  self._connection.delete(endpoint=RESOURCES_ENDPOINT, params=params)
@@ -46,7 +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_workspace_scope,
49
+ set_filter_project_scope,
50
50
  zen_store,
51
51
  )
52
52
 
@@ -81,14 +81,14 @@ def list_artifact_versions(
81
81
  Returns:
82
82
  The artifact versions according to query filters.
83
83
  """
84
- # A workspace scoped request must always be scoped to a specific
85
- # workspace. This is required for the RBAC check to work.
86
- set_filter_workspace_scope(artifact_version_filter_model)
87
- assert isinstance(artifact_version_filter_model.workspace, UUID)
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
88
 
89
89
  allowed_artifact_ids = get_allowed_resource_ids(
90
90
  resource_type=ResourceType.ARTIFACT,
91
- workspace_id=artifact_version_filter_model.workspace,
91
+ project_id=artifact_version_filter_model.project,
92
92
  )
93
93
  artifact_version_filter_model.configure_rbac(
94
94
  authenticated_user_id=auth_context.user.id,
@@ -228,24 +228,24 @@ def delete_artifact_version(
228
228
  )
229
229
  @handle_exceptions
230
230
  def prune_artifact_versions(
231
- workspace_name_or_id: Union[str, UUID],
231
+ project_name_or_id: Union[str, UUID],
232
232
  only_versions: bool = True,
233
233
  _: AuthContext = Security(authorize),
234
234
  ) -> None:
235
235
  """Prunes unused artifact versions and their artifacts.
236
236
 
237
237
  Args:
238
- workspace_name_or_id: The workspace name or ID to prune artifact
238
+ project_name_or_id: The project name or ID to prune artifact
239
239
  versions for.
240
240
  only_versions: Only delete artifact versions, keeping artifacts
241
241
  """
242
- workspace_id = zen_store().get_workspace(workspace_name_or_id).id
242
+ project_id = zen_store().get_project(project_name_or_id).id
243
243
 
244
244
  verify_permissions_and_prune_entities(
245
245
  resource_type=ResourceType.ARTIFACT_VERSION,
246
246
  prune_method=zen_store().prune_artifact_versions,
247
247
  only_versions=only_versions,
248
- workspace_id=workspace_id,
248
+ project_id=project_id,
249
249
  )
250
250
 
251
251
 
@@ -582,7 +582,7 @@ def api_token(
582
582
  f"step run {token.step_run_id}."
583
583
  )
584
584
 
585
- workspace_id: Optional[UUID] = None
585
+ project_id: Optional[UUID] = None
586
586
 
587
587
  if schedule_id:
588
588
  # The schedule must exist
@@ -593,7 +593,7 @@ def api_token(
593
593
  f"Schedule {schedule_id} does not exist and API tokens cannot "
594
594
  "be generated for non-existent schedules for security reasons."
595
595
  )
596
- workspace_id = schedule.workspace.id
596
+ project_id = schedule.project.id
597
597
 
598
598
  if not schedule.active:
599
599
  raise ValueError(
@@ -614,7 +614,7 @@ def api_token(
614
614
 
615
615
  verify_permission_for_model(model=pipeline_run, action=Action.READ)
616
616
 
617
- workspace_id = pipeline_run.workspace.id
617
+ project_id = pipeline_run.project.id
618
618
 
619
619
  if pipeline_run.status.is_finished:
620
620
  raise ValueError(
@@ -633,7 +633,7 @@ def api_token(
633
633
  "be generated for non-existent step runs for security reasons."
634
634
  )
635
635
 
636
- workspace_id = step_run.workspace.id
636
+ project_id = step_run.project.id
637
637
 
638
638
  if step_run.status.is_finished:
639
639
  raise ValueError(
@@ -642,11 +642,11 @@ def api_token(
642
642
  "for security reasons."
643
643
  )
644
644
 
645
- assert workspace_id is not None
645
+ assert project_id is not None
646
646
  verify_permission(
647
647
  resource_type=ResourceType.PIPELINE_RUN,
648
648
  action=Action.CREATE,
649
- workspace_id=workspace_id,
649
+ project_id=project_id,
650
650
  )
651
651
 
652
652
  return generate_access_token(