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.
- zenml/VERSION +1 -1
- zenml/cli/login.py +126 -50
- zenml/cli/server.py +24 -5
- zenml/config/server_config.py +142 -17
- zenml/constants.py +2 -11
- zenml/login/credentials.py +38 -14
- zenml/login/credentials_store.py +53 -18
- zenml/login/pro/client.py +3 -7
- zenml/login/pro/constants.py +0 -6
- zenml/login/pro/tenant/models.py +4 -2
- zenml/login/pro/utils.py +11 -25
- zenml/login/server_info.py +52 -0
- zenml/login/web_login.py +11 -6
- zenml/models/v2/misc/auth_models.py +1 -1
- zenml/models/v2/misc/server_models.py +44 -0
- zenml/zen_server/auth.py +97 -8
- zenml/zen_server/cloud_utils.py +79 -87
- zenml/zen_server/csrf.py +91 -0
- zenml/zen_server/deploy/helm/templates/NOTES.txt +22 -0
- zenml/zen_server/deploy/helm/templates/_environment.tpl +50 -24
- zenml/zen_server/deploy/helm/templates/server-secret.yaml +11 -0
- zenml/zen_server/deploy/helm/values.yaml +76 -7
- zenml/zen_server/feature_gate/feature_gate_interface.py +1 -1
- zenml/zen_server/jwt.py +16 -1
- zenml/zen_server/rbac/endpoint_utils.py +3 -3
- zenml/zen_server/routers/auth_endpoints.py +44 -21
- zenml/zen_server/routers/models_endpoints.py +1 -2
- zenml/zen_server/routers/pipelines_endpoints.py +2 -2
- zenml/zen_server/routers/stack_deployment_endpoints.py +5 -5
- zenml/zen_server/routers/workspaces_endpoints.py +2 -2
- zenml/zen_server/utils.py +64 -0
- zenml/zen_server/zen_server_api.py +5 -0
- zenml/zen_stores/base_zen_store.py +19 -1
- zenml/zen_stores/rest_zen_store.py +30 -20
- {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/METADATA +3 -1
- {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/RECORD +39 -37
- {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.72.0.dev20250115.dist-info → zenml_nightly-0.72.0.dev20250117.dist-info}/WHEEL +0 -0
- {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
|
-
"""
|
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
|
-
"""
|
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
|
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
|
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
|
-
#
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
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
|
284
|
+
if not authorization_header:
|
281
285
|
logger.info(
|
282
|
-
"External
|
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=
|
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
|
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
|
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
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
527
|
+
ResourceType.PIPELINE in server_config().reportable_resources
|
528
528
|
and zen_store().count_pipelines(PipelineFilter(name=pipeline.name))
|
529
529
|
== 0
|
530
530
|
)
|