zenml-nightly 0.72.0.dev20250115__py3-none-any.whl → 0.72.0.dev20250117__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 (39) hide show
  1. zenml/VERSION +1 -1
  2. zenml/cli/login.py +126 -50
  3. zenml/cli/server.py +24 -5
  4. zenml/config/server_config.py +142 -17
  5. zenml/constants.py +2 -11
  6. zenml/login/credentials.py +38 -14
  7. zenml/login/credentials_store.py +53 -18
  8. zenml/login/pro/client.py +3 -7
  9. zenml/login/pro/constants.py +0 -6
  10. zenml/login/pro/tenant/models.py +4 -2
  11. zenml/login/pro/utils.py +11 -25
  12. zenml/login/server_info.py +52 -0
  13. zenml/login/web_login.py +11 -6
  14. zenml/models/v2/misc/auth_models.py +1 -1
  15. zenml/models/v2/misc/server_models.py +44 -0
  16. zenml/zen_server/auth.py +97 -8
  17. zenml/zen_server/cloud_utils.py +79 -87
  18. zenml/zen_server/csrf.py +91 -0
  19. zenml/zen_server/deploy/helm/templates/NOTES.txt +22 -0
  20. zenml/zen_server/deploy/helm/templates/_environment.tpl +50 -24
  21. zenml/zen_server/deploy/helm/templates/server-secret.yaml +11 -0
  22. zenml/zen_server/deploy/helm/values.yaml +76 -7
  23. zenml/zen_server/feature_gate/feature_gate_interface.py +1 -1
  24. zenml/zen_server/jwt.py +16 -1
  25. zenml/zen_server/rbac/endpoint_utils.py +3 -3
  26. zenml/zen_server/routers/auth_endpoints.py +44 -21
  27. zenml/zen_server/routers/models_endpoints.py +1 -2
  28. zenml/zen_server/routers/pipelines_endpoints.py +2 -2
  29. zenml/zen_server/routers/stack_deployment_endpoints.py +5 -5
  30. zenml/zen_server/routers/workspaces_endpoints.py +2 -2
  31. zenml/zen_server/utils.py +64 -0
  32. zenml/zen_server/zen_server_api.py +5 -0
  33. zenml/zen_stores/base_zen_store.py +19 -1
  34. zenml/zen_stores/rest_zen_store.py +30 -20
  35. {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/METADATA +3 -1
  36. {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/RECORD +39 -37
  37. {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/LICENSE +0 -0
  38. {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/WHEEL +0 -0
  39. {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/entry_points.txt +0 -0
@@ -140,8 +140,58 @@ Returns:
140
140
  A dictionary with the non-secret values configured for the ZenML server.
141
141
  */}}
142
142
  {{- define "zenml.serverConfigurationAttrs" -}}
143
+
144
+ {{- if .ZenML.pro.enabled }}
145
+ deployment_type: cloud
146
+ pro_api_url: "{{ .ZenML.pro.apiURL }}"
147
+ pro_dashboard_url: "{{ .ZenML.pro.dashboardURL }}"
148
+ pro_oauth2_audience: "{{ .ZenML.pro.apiURL }}"
149
+ pro_organization_id: "{{ .ZenML.pro.organizationID }}"
150
+ pro_tenant_id: "{{ .ZenML.pro.tenantID }}"
151
+ {{- if .ZenML.pro.tenantName }}
152
+ pro_tenant_name: "{{ .ZenML.pro.tenantName }}"
153
+ {{- end }}
154
+ {{- if .ZenML.pro.organizationName }}
155
+ pro_organization_name: "{{ .ZenML.pro.organizationName }}"
156
+ {{- end }}
157
+ {{- if .ZenML.pro.extraCorsOrigins }}
158
+ cors_allow_origins: "{{ join "," .ZenML.pro.extraCorsOrigins }}"
159
+ {{- end }}
160
+ {{- if .ZenML.auth.jwtTokenExpireMinutes }}
161
+ jwt_token_expire_minutes: {{ .ZenML.auth.jwtTokenExpireMinutes | quote }}
162
+ {{- end }}
163
+
164
+ {{- else }}
165
+
143
166
  auth_scheme: {{ .ZenML.authType | default .ZenML.auth.authType | quote }}
144
167
  deployment_type: {{ .ZenML.deploymentType | default "kubernetes" }}
168
+ {{- if .ZenML.auth.corsAllowOrigins }}
169
+ cors_allow_origins: {{ join "," .ZenML.auth.corsAllowOrigins | quote }}
170
+ {{- end }}
171
+ {{- if .ZenML.auth.externalLoginURL }}
172
+ external_login_url: {{ .ZenML.auth.externalLoginURL | quote }}
173
+ {{- end }}
174
+ {{- if .ZenML.auth.externalUserInfoURL }}
175
+ external_user_info_url: {{ .ZenML.auth.externalUserInfoURL | quote }}
176
+ {{- end }}
177
+ {{- if .ZenML.auth.externalServerID }}
178
+ external_server_id: {{ .ZenML.auth.externalServerID | quote }}
179
+ {{- end }}
180
+ {{- if .ZenML.auth.jwtTokenExpireMinutes }}
181
+ jwt_token_expire_minutes: {{ .ZenML.auth.jwtTokenExpireMinutes | quote }}
182
+ {{- end }}
183
+ {{- if .ZenML.auth.rbacImplementationSource }}
184
+ rbac_implementation_source: {{ .ZenML.auth.rbacImplementationSource | quote }}
185
+ {{- end }}
186
+ {{- if .ZenML.auth.featureGateImplementationSource }}
187
+ feature_gate_implementation_source: {{ .ZenML.auth.featureGateImplementationSource | quote }}
188
+ {{- end }}
189
+ {{- if .ZenML.dashboardURL }}
190
+ dashboard_url: {{ .ZenML.dashboardURL | quote }}
191
+ {{- end }}
192
+
193
+ {{- end }}
194
+
145
195
  {{- if .ZenML.threadPoolSize }}
146
196
  thread_pool_size: {{ .ZenML.threadPoolSize | quote }}
147
197
  {{- end }}
@@ -157,18 +207,12 @@ jwt_token_audience: {{ .ZenML.auth.jwtTokenAudience | quote }}
157
207
  {{- if .ZenML.auth.jwtTokenLeewaySeconds }}
158
208
  jwt_token_leeway_seconds: {{ .ZenML.auth.jwtTokenLeewaySeconds | quote }}
159
209
  {{- end }}
160
- {{- if .ZenML.auth.jwtTokenExpireMinutes }}
161
- jwt_token_expire_minutes: {{ .ZenML.auth.jwtTokenExpireMinutes | quote }}
162
- {{- end }}
163
210
  {{- if .ZenML.auth.authCookieName }}
164
211
  auth_cookie_name: {{ .ZenML.auth.authCookieName | quote }}
165
212
  {{- end }}
166
213
  {{- if .ZenML.auth.authCookieDomain }}
167
214
  auth_cookie_domain: {{ .ZenML.auth.authCookieDomain | quote }}
168
215
  {{- end }}
169
- {{- if .ZenML.auth.corsAllowOrigins }}
170
- cors_allow_origins: {{ join "," .ZenML.auth.corsAllowOrigins | quote }}
171
- {{- end }}
172
216
  {{- if .ZenML.auth.maxFailedDeviceAuthAttempts }}
173
217
  max_failed_device_auth_attempts: {{ .ZenML.auth.maxFailedDeviceAuthAttempts | quote }}
174
218
  {{- end }}
@@ -184,30 +228,12 @@ device_expiration_minutes: {{ .ZenML.auth.deviceExpirationMinutes | quote }}
184
228
  {{- if .ZenML.auth.trustedDeviceExpirationMinutes }}
185
229
  trusted_device_expiration_minutes: {{ .ZenML.auth.trustedDeviceExpirationMinutes | quote }}
186
230
  {{- end }}
187
- {{- if .ZenML.auth.externalLoginURL }}
188
- external_login_url: {{ .ZenML.auth.externalLoginURL | quote }}
189
- {{- end }}
190
- {{- if .ZenML.auth.externalUserInfoURL }}
191
- external_user_info_url: {{ .ZenML.auth.externalUserInfoURL | quote }}
192
- {{- end }}
193
- {{- if .ZenML.auth.externalCookieName }}
194
- external_cookie_name: {{ .ZenML.auth.externalCookieName | quote }}
195
- {{- end }}
196
- {{- if .ZenML.auth.externalServerID }}
197
- external_server_id: {{ .ZenML.auth.externalServerID | quote }}
198
- {{- end }}
199
231
  {{- if .ZenML.rootUrlPath }}
200
232
  root_url_path: {{ .ZenML.rootUrlPath | quote }}
201
233
  {{- end }}
202
234
  {{- if .ZenML.serverURL }}
203
235
  server_url: {{ .ZenML.serverURL | quote }}
204
236
  {{- end }}
205
- {{- if .ZenML.dashboardURL }}
206
- dashboard_url: {{ .ZenML.dashboardURL | quote }}
207
- {{- end }}
208
- {{- if .ZenML.auth.rbacImplementationSource }}
209
- rbac_implementation_source: {{ .ZenML.auth.rbacImplementationSource | quote }}
210
- {{- end }}
211
237
  {{- range $key, $value := .ZenML.secure_headers }}
212
238
  secure_headers_{{ $key }}: {{ $value | quote }}
213
239
  {{- end }}
@@ -10,6 +10,17 @@ data:
10
10
  {{- else }}
11
11
  ZENML_SERVER_JWT_SECRET_KEY: {{ $prevServerSecret.data.ZENML_SERVER_JWT_SECRET_KEY | default (randAlphaNum 32 | b64enc | quote) }}
12
12
  {{- end }}
13
+
14
+ {{- if .Values.zenml.pro.enabled }}
15
+ {{- if .Values.zenml.pro.enrollmentKey }}
16
+ ZENML_SERVER_PRO_OAUTH2_CLIENT_SECRET: {{ .Values.zenml.pro.enrollmentKey | b64enc | quote }}
17
+ {{- else if or .Release.IsInstall (not $prevServerSecret) }}
18
+ ZENML_SERVER_PRO_OAUTH2_CLIENT_SECRET: {{ randAlphaNum 64 | b64enc | quote }}
19
+ {{- else }}
20
+ ZENML_SERVER_PRO_OAUTH2_CLIENT_SECRET: {{ $prevServerSecret.data.ZENML_SERVER_PRO_OAUTH2_CLIENT_SECRET | default (randAlphaNum 64 | b64enc | quote) }}
21
+ {{- end }}
22
+ {{- end }}
23
+
13
24
  {{- range $k, $v := include "zenml.storeSecretEnvVariables" . | fromYaml}}
14
25
  {{ $k }}: {{ $v | b64enc | quote }}
15
26
  {{- end }}
@@ -27,8 +27,43 @@ zenml:
27
27
  # Overrides the image tag whose default is the chart appVersion.
28
28
  tag:
29
29
 
30
+ # ZenML Pro related options.
31
+ pro:
32
+ # Set `enabled` to true to enable ZenML Pro servers. If set, some of the
33
+ # configuration options in the `zenml` section will be overridden with
34
+ # values specific to ZenML Pro servers computed from the values set in the
35
+ # `pro` section.
36
+ enabled: false
37
+
38
+ # The URL where the ZenML Pro server API is reachable
39
+ apiURL: https://cloudapi.zenml.io
40
+
41
+ # The URL where the ZenML Pro dashboard is reachable.
42
+ dashboardURL: https://cloud.zenml.io
43
+
44
+ # Additional origins to allow in the CORS policy.
45
+ extraCorsOrigins:
46
+
47
+ # The ID of the ZenML Pro tenant to use.
48
+ tenantID:
49
+
50
+ # The name of the ZenML Pro tenant to use.
51
+ tenantName:
52
+
53
+ # The ID of the ZenML Pro organization to use.
54
+ organizationID:
55
+
56
+ # The name of the ZenML Pro organization to use.
57
+ organizationName:
58
+
59
+ # The enrollment key to use for the ZenML Pro tenant. If not specified,
60
+ # an enrollment key will be auto-generated.
61
+ enrollmentKey:
62
+
30
63
  # The URL where the ZenML server API is reachable. If not specified, the
31
64
  # clients will use the same URL used to connect them to the ZenML server.
65
+ #
66
+ # IMPORTANT: this value must be set for ZenML Pro servers.
32
67
  serverURL:
33
68
 
34
69
  # The URL where the ZenML dashboard is reachable.
@@ -39,6 +74,8 @@ zenml:
39
74
  # This is value is used to compute the dashboard URLs during the web login
40
75
  # authentication workflow, to print dashboard URLs in log messages when
41
76
  # running a pipeline and for other similar tasks.
77
+ #
78
+ # This value is overridden if the `zenml.pro.enabled` value is set.
42
79
  dashboardURL:
43
80
 
44
81
  debug: true
@@ -48,6 +85,8 @@ zenml:
48
85
 
49
86
  # ZenML server deployment type. This field is used for telemetry purposes.
50
87
  # Example values are "local", "kubernetes", "aws", "gcp", "azure".
88
+ #
89
+ # This value is overridden if the `zenml.pro.enabled` value is set.
51
90
  deploymentType:
52
91
 
53
92
  # Authentication settings that control how the ZenML server authenticates
@@ -60,6 +99,8 @@ zenml:
60
99
  # HTTP_BASIC - HTTP Basic authentication
61
100
  # OAUTH2_PASSWORD_BEARER - OAuth2 password bearer
62
101
  # EXTERNAL - External authentication (e.g. via a remote authenticator)
102
+ #
103
+ # This value is overridden if the `zenml.pro.enabled` value is set.
63
104
  authType: OAUTH2_PASSWORD_BEARER
64
105
 
65
106
  # The secret key used to sign JWT tokens. This should be set to
@@ -91,7 +132,6 @@ zenml:
91
132
  # ZenML Server ID.
92
133
  jwtTokenIssuer:
93
134
 
94
-
95
135
  # The audience of the JWT tokens. If not specified, the audience is set to
96
136
  # the ZenML Server ID.
97
137
  jwtTokenAudience:
@@ -102,6 +142,8 @@ zenml:
102
142
 
103
143
  # The expiration time of JWT tokens in minutes. If not specified, generated
104
144
  # JWT tokens will not be set to expire.
145
+ #
146
+ # This value is automatically set if the `zenml.pro.enabled` value is set.
105
147
  jwtTokenExpireMinutes:
106
148
 
107
149
  # The name of the http-only cookie used to store the JWT tokens used to
@@ -117,20 +159,31 @@ zenml:
117
159
  # The origins allowed to make cross-origin requests to the ZenML server. If
118
160
  # not specified, all origins are allowed. Set this when the ZenML dashboard
119
161
  # is hosted on a different domain than the ZenML server.
162
+ #
163
+ # This value is overridden if the `zenml.pro.enabled` value is set.
120
164
  corsAllowOrigins:
121
165
  - "*"
122
166
 
123
167
  # The maximum number of failed authentication attempts allowed for an OAuth
124
168
  # 2.0 device before the device is locked.
169
+ #
170
+ # This value is ignored if the `zenml.auth.authType` is set to `EXTERNAL` or
171
+ # `NO_AUTH`.
125
172
  maxFailedDeviceAuthAttempts: 3
126
173
 
127
174
  # The timeout in seconds after which a pending OAuth 2.0 device
128
175
  # authorization request expires.
176
+ #
177
+ # This value is ignored if the `zenml.auth.authType` is set to `EXTERNAL` or
178
+ # `NO_AUTH`.
129
179
  deviceAuthTimeout: 300
130
180
 
131
181
  # The polling interval in seconds used by clients to poll the OAuth 2.0
132
182
  # device authorization endpoint for the status of a pending device
133
183
  # authorization request.
184
+ #
185
+ # This value is ignored if the `zenml.auth.authType` is set to `EXTERNAL` or
186
+ # `NO_AUTH`.
134
187
  deviceAuthPollingInterval: 5
135
188
 
136
189
  # The time in minutes that an OAuth 2.0 device is allowed to be used to
@@ -139,6 +192,9 @@ zenml:
139
192
  # be used indefinitely. This controls the expiration time of the JWT tokens
140
193
  # issued to clients after they have authenticated with the ZenML server
141
194
  # using an OAuth 2.0 device.
195
+ #
196
+ # This value is ignored if the `zenml.auth.authType` is set to `EXTERNAL` or
197
+ # `NO_AUTH`.
142
198
  deviceExpirationMinutes:
143
199
 
144
200
  # The time in minutes that a trusted OAuth 2.0 device is allowed to be used
@@ -147,33 +203,46 @@ zenml:
147
203
  # be used indefinitely. This controls the expiration time of the JWT tokens
148
204
  # issued to clients after they have authenticated with the ZenML server
149
205
  # using an OAuth 2.0 device that was previously trusted by the user.
206
+ #
207
+ # This value is ignored if the `zenml.auth.authType` is set to `EXTERNAL` or
208
+ # `NO_AUTH`.
150
209
  trustedDeviceExpirationMinutes:
151
210
 
152
211
  # The login URL of an external authenticator service to use with the
153
212
  # `EXTERNAL` authentication scheme. Only relevant if `zenml.auth.authType`
154
213
  # is set to `EXTERNAL`.
214
+ #
215
+ # This value is overridden if the `zenml.pro.enabled` value is set.
155
216
  externalLoginURL:
156
217
 
157
218
  # The user info URL of an external authenticator service to use with the
158
219
  # `EXTERNAL` authentication scheme. Only relevant if `zenml.auth.authType`
159
220
  # is set to `EXTERNAL`.
221
+ #
222
+ # This value is overridden if the `zenml.pro.enabled` value is set.
160
223
  externalUserInfoURL:
161
224
 
162
- # The name of the http-only cookie used to store the bearer token used to
163
- # authenticate with the external authenticator service. Only relevant if
164
- # `zenml.auth.authType` is set to `EXTERNAL`.
165
- externalCookieName:
166
-
167
225
  # The UUID of the ZenML server to use with the `EXTERNAL` authentication
168
226
  # scheme. If not specified, the regular ZenML server ID (deployment ID) is
169
227
  # used.
228
+ #
229
+ # This value is overridden if the `zenml.pro.enabled` value is set.
170
230
  externalServerID:
171
231
 
172
232
  # Source pointing to a class implementing the RBAC interface defined by
173
- # `zenml.zen_server.rbac_interface.RBACInterface`. If not specified,
233
+ # `zenml.zen_server.rbac.rbac_interface.RBACInterface`. If not specified,
174
234
  # RBAC will not be enabled for this server.
235
+ #
236
+ # This value is overridden if the `zenml.pro.enabled` value is set.
175
237
  rbacImplementationSource:
176
238
 
239
+ # Source pointing to a class implementing the feature gate interface defined
240
+ # by `zenml.zen_server.feature_gate.feature_gate_interface.FeatureGateInterface`.
241
+ # If not specified, feature gating will not be enabled for this server.
242
+ #
243
+ # This value is overridden if the `zenml.pro.enabled` value is set.
244
+ featureGateImplementationSource:
245
+
177
246
  # The root URL path to use when behind a proxy. This is useful when the
178
247
  # `rewrite-target` annotation is used in the ingress controller, e.g.:
179
248
  #
@@ -20,7 +20,7 @@ from zenml.zen_server.rbac.models import ResourceType
20
20
 
21
21
 
22
22
  class FeatureGateInterface(ABC):
23
- """RBAC interface definition."""
23
+ """Feature gate interface definition."""
24
24
 
25
25
  @abstractmethod
26
26
  def check_entitlement(self, resource: ResourceType) -> None:
zenml/zen_server/jwt.py CHANGED
@@ -11,7 +11,7 @@
11
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
12
  # or implied. See the License for the specific language governing
13
13
  # permissions and limitations under the License.
14
- """Authentication module for ZenML server."""
14
+ """JWT utilities module for ZenML server."""
15
15
 
16
16
  from datetime import datetime, timedelta
17
17
  from typing import (
@@ -45,6 +45,7 @@ class JWTToken(BaseModel):
45
45
  issued.
46
46
  step_run_id: The id of the step run for which the token was
47
47
  issued.
48
+ session_id: The id of the authenticated session (used for CSRF).
48
49
  claims: The original token claims.
49
50
  """
50
51
 
@@ -54,6 +55,7 @@ class JWTToken(BaseModel):
54
55
  schedule_id: Optional[UUID] = None
55
56
  pipeline_run_id: Optional[UUID] = None
56
57
  step_run_id: Optional[UUID] = None
58
+ session_id: Optional[UUID] = None
57
59
  claims: Dict[str, Any] = {}
58
60
 
59
61
  @classmethod
@@ -156,6 +158,16 @@ class JWTToken(BaseModel):
156
158
  "UUID"
157
159
  )
158
160
 
161
+ session_id: Optional[UUID] = None
162
+ if "session_id" in claims:
163
+ try:
164
+ session_id = UUID(claims.pop("session_id"))
165
+ except ValueError:
166
+ raise CredentialsNotValid(
167
+ "Invalid JWT token: the session_id claim is not a valid "
168
+ "UUID"
169
+ )
170
+
159
171
  return JWTToken(
160
172
  user_id=user_id,
161
173
  device_id=device_id,
@@ -163,6 +175,7 @@ class JWTToken(BaseModel):
163
175
  schedule_id=schedule_id,
164
176
  pipeline_run_id=pipeline_run_id,
165
177
  step_run_id=step_run_id,
178
+ session_id=session_id,
166
179
  claims=claims,
167
180
  )
168
181
 
@@ -201,6 +214,8 @@ class JWTToken(BaseModel):
201
214
  claims["pipeline_run_id"] = str(self.pipeline_run_id)
202
215
  if self.step_run_id:
203
216
  claims["step_run_id"] = str(self.step_run_id)
217
+ if self.session_id:
218
+ claims["session_id"] = str(self.session_id)
204
219
 
205
220
  return jwt.encode(
206
221
  claims,
@@ -19,7 +19,6 @@ from uuid import UUID
19
19
  from pydantic import BaseModel
20
20
 
21
21
  from zenml.constants import (
22
- REPORTABLE_RESOURCES,
23
22
  REQUIRES_CUSTOM_RESOURCE_REPORTING,
24
23
  )
25
24
  from zenml.exceptions import IllegalOperationError
@@ -43,6 +42,7 @@ from zenml.zen_server.rbac.utils import (
43
42
  verify_permission,
44
43
  verify_permission_for_model,
45
44
  )
45
+ from zenml.zen_server.utils import server_config
46
46
 
47
47
  AnyRequest = TypeVar("AnyRequest", bound=BaseRequest)
48
48
  AnyResponse = TypeVar("AnyResponse", bound=BaseIdentifiedResponse) # type: ignore[type-arg]
@@ -82,7 +82,7 @@ def verify_permissions_and_create_entity(
82
82
  verify_permission(resource_type=resource_type, action=Action.CREATE)
83
83
 
84
84
  needs_usage_increment = (
85
- resource_type in REPORTABLE_RESOURCES
85
+ resource_type in server_config().reportable_resources
86
86
  and resource_type not in REQUIRES_CUSTOM_RESOURCE_REPORTING
87
87
  )
88
88
  if needs_usage_increment:
@@ -129,7 +129,7 @@ def verify_permissions_and_batch_create_entity(
129
129
 
130
130
  verify_permission(resource_type=resource_type, action=Action.CREATE)
131
131
 
132
- if resource_type in REPORTABLE_RESOURCES:
132
+ if resource_type in server_config().reportable_resources:
133
133
  raise RuntimeError(
134
134
  "Batch requests are currently not possible with usage-tracked features."
135
135
  )
@@ -175,6 +175,12 @@ class OAuthLoginRequestForm:
175
175
  self.username = username
176
176
  self.password = password or ""
177
177
  elif self.grant_type == OAuthGrantTypes.OAUTH_DEVICE_CODE:
178
+ if config.auth_scheme in [AuthScheme.NO_AUTH, AuthScheme.EXTERNAL]:
179
+ raise HTTPException(
180
+ status_code=status.HTTP_403_FORBIDDEN,
181
+ detail="Device authorization is not supported.",
182
+ )
183
+
178
184
  if not device_code or not client_id:
179
185
  logger.info("Request with missing device code or client ID")
180
186
  raise HTTPException(
@@ -237,6 +243,8 @@ def token(
237
243
  ValueError: If the grant type is invalid.
238
244
  """
239
245
  config = server_config()
246
+ cookie_response: Optional[Response] = response
247
+
240
248
  if auth_form_data.grant_type == OAuthGrantTypes.OAUTH_PASSWORD:
241
249
  auth_context = authenticate_credentials(
242
250
  user_name_or_id=auth_form_data.username,
@@ -248,38 +256,34 @@ def token(
248
256
  client_id=auth_form_data.client_id,
249
257
  device_code=auth_form_data.device_code,
250
258
  )
259
+ # API tokens for authorized device are only meant for non-web clients
260
+ # and should not be stored as cookies
261
+ cookie_response = None
262
+
251
263
  elif auth_form_data.grant_type == OAuthGrantTypes.ZENML_API_KEY:
252
264
  auth_context = authenticate_api_key(
253
265
  api_key=auth_form_data.api_key,
254
266
  )
267
+ # API tokens for API keys are only meant for non-web clients
268
+ # and should not be stored as cookies
269
+ cookie_response = None
255
270
 
256
271
  elif auth_form_data.grant_type == OAuthGrantTypes.ZENML_EXTERNAL:
257
- assert config.external_cookie_name is not None
258
272
  assert config.external_login_url is not None
259
273
 
260
274
  authorization_url = config.external_login_url
261
275
 
262
- # First, try to get the external access token from the external cookie
263
- external_access_token = request.cookies.get(
264
- config.external_cookie_name
265
- )
266
- if not external_access_token:
267
- # Next, try to get the external access token from the authorization
268
- # header
269
- authorization_header = request.headers.get("Authorization")
270
- if authorization_header:
271
- scheme, _, token = authorization_header.partition(" ")
272
- if token and scheme.lower() == "bearer":
273
- external_access_token = token
274
- logger.info(
275
- "External access token found in authorization header."
276
- )
277
- else:
278
- logger.info("External access token found in cookie.")
276
+ # Try to get the external session token or authorization token from the
277
+ # authorization header
278
+ authorization_header = request.headers.get("Authorization")
279
+ if authorization_header:
280
+ scheme, _, token = authorization_header.partition(" ")
281
+ if token and scheme.lower() == "bearer":
282
+ external_access_token = token
279
283
 
280
- if not external_access_token:
284
+ if not authorization_header:
281
285
  logger.info(
282
- "External access token not found. Redirecting to "
286
+ "External session or authorization token not found. Redirecting to "
283
287
  "external authenticator."
284
288
  )
285
289
 
@@ -291,13 +295,18 @@ def token(
291
295
  request=request,
292
296
  )
293
297
 
298
+ # TODO: no easy way to detect which type of client issued this request
299
+ # (web or non-web) in order to decide whether to store the access token
300
+ # as a cookie in the response or not. For now, we always assume a web
301
+ # client.
294
302
  else:
295
303
  # Shouldn't happen, because we verify all grants in the form data
296
304
  raise ValueError("Invalid grant type.")
297
305
 
298
306
  return generate_access_token(
299
307
  user_id=auth_context.user.id,
300
- response=response,
308
+ response=cookie_response,
309
+ request=request,
301
310
  device=auth_context.device,
302
311
  api_key=auth_context.api_key,
303
312
  )
@@ -351,8 +360,18 @@ def device_authorization(
351
360
 
352
361
  Returns:
353
362
  The device authorization response.
363
+
364
+ Raises:
365
+ HTTPException: If the device authorization is not supported.
354
366
  """
355
367
  config = server_config()
368
+
369
+ if config.auth_scheme in [AuthScheme.NO_AUTH, AuthScheme.EXTERNAL]:
370
+ raise HTTPException(
371
+ status_code=status.HTTP_403_FORBIDDEN,
372
+ detail="Device authorization is not supported.",
373
+ )
374
+
356
375
  store = zen_store()
357
376
 
358
377
  try:
@@ -521,6 +540,8 @@ def api_token(
521
540
  return generate_access_token(
522
541
  user_id=token.user_id,
523
542
  expires_in=expires_in,
543
+ # Don't include the access token as a cookie in the response
544
+ response=None,
524
545
  ).access_token
525
546
 
526
547
  verify_permission(
@@ -621,6 +642,8 @@ def api_token(
621
642
  schedule_id=schedule_id,
622
643
  pipeline_run_id=pipeline_run_id,
623
644
  step_run_id=step_run_id,
645
+ # Don't include the access token as a cookie in the response
646
+ response=None,
624
647
  # Never expire the token
625
648
  expires_in=0,
626
649
  ).access_token
@@ -22,7 +22,6 @@ from zenml.constants import (
22
22
  API,
23
23
  MODEL_VERSIONS,
24
24
  MODELS,
25
- REPORTABLE_RESOURCES,
26
25
  VERSION_1,
27
26
  )
28
27
  from zenml.models import (
@@ -170,7 +169,7 @@ def delete_model(
170
169
  )
171
170
 
172
171
  if server_config().feature_gate_enabled:
173
- if ResourceType.MODEL in REPORTABLE_RESOURCES:
172
+ if ResourceType.MODEL in server_config().reportable_resources:
174
173
  report_decrement(ResourceType.MODEL, resource_id=model.id)
175
174
 
176
175
 
@@ -20,7 +20,6 @@ from fastapi import APIRouter, Depends, Security
20
20
  from zenml.constants import (
21
21
  API,
22
22
  PIPELINES,
23
- REPORTABLE_RESOURCES,
24
23
  RUNS,
25
24
  VERSION_1,
26
25
  )
@@ -45,6 +44,7 @@ from zenml.zen_server.rbac.models import ResourceType
45
44
  from zenml.zen_server.utils import (
46
45
  handle_exceptions,
47
46
  make_dependable,
47
+ server_config,
48
48
  zen_store,
49
49
  )
50
50
 
@@ -159,7 +159,7 @@ def delete_pipeline(
159
159
  )
160
160
 
161
161
  should_decrement = (
162
- ResourceType.PIPELINE in REPORTABLE_RESOURCES
162
+ ResourceType.PIPELINE in server_config().reportable_resources
163
163
  and zen_store().count_pipelines(PipelineFilter(name=pipeline.name))
164
164
  == 0
165
165
  )
@@ -34,7 +34,7 @@ from zenml.models import (
34
34
  StackDeploymentInfo,
35
35
  )
36
36
  from zenml.stack_deployments.utils import get_stack_deployment_class
37
- from zenml.zen_server.auth import AuthContext, authorize
37
+ from zenml.zen_server.auth import AuthContext, authorize, generate_access_token
38
38
  from zenml.zen_server.exceptions import error_response
39
39
  from zenml.zen_server.rbac.models import Action, ResourceType
40
40
  from zenml.zen_server.rbac.utils import verify_permission
@@ -114,10 +114,10 @@ def get_stack_deployment_config(
114
114
  assert token is not None
115
115
 
116
116
  # A new API token is generated for the stack deployment
117
- expires = datetime.datetime.utcnow() + datetime.timedelta(
118
- minutes=STACK_DEPLOYMENT_API_TOKEN_EXPIRATION
119
- )
120
- api_token = token.encode(expires=expires)
117
+ api_token = generate_access_token(
118
+ user_id=token.user_id,
119
+ expires_in=STACK_DEPLOYMENT_API_TOKEN_EXPIRATION * 60,
120
+ ).access_token
121
121
 
122
122
  return stack_deployment_class(
123
123
  terraform=terraform,
@@ -27,7 +27,6 @@ from zenml.constants import (
27
27
  PIPELINE_BUILDS,
28
28
  PIPELINE_DEPLOYMENTS,
29
29
  PIPELINES,
30
- REPORTABLE_RESOURCES,
31
30
  RUN_METADATA,
32
31
  RUN_TEMPLATES,
33
32
  RUNS,
@@ -112,6 +111,7 @@ from zenml.zen_server.rbac.utils import (
112
111
  from zenml.zen_server.utils import (
113
112
  handle_exceptions,
114
113
  make_dependable,
114
+ server_config,
115
115
  zen_store,
116
116
  )
117
117
 
@@ -524,7 +524,7 @@ def create_pipeline(
524
524
 
525
525
  # We limit pipeline namespaces, not pipeline versions
526
526
  needs_usage_increment = (
527
- ResourceType.PIPELINE in REPORTABLE_RESOURCES
527
+ ResourceType.PIPELINE in server_config().reportable_resources
528
528
  and zen_store().count_pipelines(PipelineFilter(name=pipeline.name))
529
529
  == 0
530
530
  )