feat: allow auth overrides in debug menu#1110
Conversation
1d0c032 to
4859263
Compare
christian-heusel
left a comment
There was a problem hiding this comment.
Hey @andyatmiami thanks a ton for staring to tackle this! 🔥
I took a brief look at this (knowing it is still in a draft state) and have the following two comments as of now (which are unrelated to the code itself):
- It is currently unclear what valid values would be for the two fields, we should either make this a dropdown, suggest values or let developers using this know in a help/hover text
- We should most likely also add documentation to the development guide once everything is wired up to let people know of this new "impersonation" feature
4859263 to
a288dc1
Compare
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
a288dc1 to
f3a6532
Compare
|
/ok-to-test |
Backend auth is enabled by default in the Tilt dev environment (DISABLE_AUTH=false). Contributors need a way to set the kubeflow-userid and kubeflow-groups request headers from the frontend UI, and the dev cluster needs RBAC and access logging infrastructure to support end-to-end auth testing. Frontend (debug auth menu): - Add devAuth.ts utility with localStorage helpers and Axios interceptor - Add DebugAuthSection component with User/Groups form fields - Replace Debug page placeholder with the new auth section - Register interceptor on all API instances in notebookApisImpl - Add unit tests for all devAuth utility functions (12 tests) Dev infrastructure (developing/): - Add RBAC manifests for two dev users: "admin" (cluster-wide via kubeflow-workspaces-admin + supplement) and "user" (namespace-scoped in default via kubeflow-workspaces-edit + supplement), using the controller-defined ClusterRoles for realistic Kubeflow RBAC behavior - Add Istio access logging with structured JSON output including kubeflow-userid and kubeflow-groups headers, using MeshConfig extension provider and Telemetry API - Extract istioctl install flags into IstioOperator values file (developing/manifests/istio-install-values.yaml) - Move kind.yaml from scripts/ to manifests/ alongside other config - Update Tiltfile with dev-rbac and istio-gateway-logging resources - Document RBAC users and access logging in DEVELOPMENT_GUIDE.md Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Andy Stoneberg <astonebe@redhat.com>
f3a6532 to
ea41a61
Compare
christian-heusel
left a comment
There was a problem hiding this comment.
Hey andy, really awesome that you're pushing this (as always: everything, everywhere, all at once 🚀 ) because it will greatly improve the developer experience and allow us to catch entire categories of bugs upfront. 🔥
I currently don't have access to my developer machine, so I can only leave the following (minor) feedback so far and my review is code-only 😄
| Example log entry: | ||
|
|
||
| ```json | ||
| {"duration":105,"kubeflow_groups":null,"kubeflow_userid":"admin","method":"GET","path":"/workspaces/api/v1/workspaces/default","response_code":200,"timestamp":"2026-05-21T14:12:11.965Z","upstream":"10.244.1.9:4000"} |
There was a problem hiding this comment.
I think to showcase the contained information within the log it would make more sense to format this log entry like this:
| {"duration":105,"kubeflow_groups":null,"kubeflow_userid":"admin","method":"GET","path":"/workspaces/api/v1/workspaces/default","response_code":200,"timestamp":"2026-05-21T14:12:11.965Z","upstream":"10.244.1.9:4000"} | |
| { | |
| "duration": 105, | |
| "kubeflow_groups": null, | |
| "kubeflow_userid": "admin", | |
| "method": "GET", | |
| "path": "/workspaces/api/v1/workspaces/default", | |
| "response_code": 200, | |
| "timestamp": "2026-05-21T14:12:11.965Z", | |
| "upstream": "10.244.1.9:4000" | |
| } |
| -f "${DEVELOPING_DIR}/manifests/istio-install-values.yaml" \ | ||
| -y |
There was a problem hiding this comment.
I prefer the longopts here for readability:
| -f "${DEVELOPING_DIR}/manifests/istio-install-values.yaml" \ | |
| -y | |
| --filename "${DEVELOPING_DIR}/manifests/istio-install-values.yaml" \ | |
| --skip-confirmation |
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| KIND_CONFIG="${SCRIPT_DIR}/kind.yaml" | ||
| DEVELOPING_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" | ||
| KIND_CONFIG="${DEVELOPING_DIR}/manifests/kind.yaml" |
There was a problem hiding this comment.
It's a bit weird how we define those paths (even though I realize that this is the way we did it beforehand, I think a cleaner way would be something like realpath developing/scripts, potentially we could modify this to drop the cd && pwd pattern. Also SCRIPT_DIR is unused with this change but it's a standard definition we seem to put in our scripts, so we could perhaps collapse it into the following:
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | |
| KIND_CONFIG="${SCRIPT_DIR}/kind.yaml" | |
| DEVELOPING_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" | |
| KIND_CONFIG="${DEVELOPING_DIR}/manifests/kind.yaml" | |
| SCRIPT_DIR="$(realpath "$(dirname "${BASH_SOURCE[0]}")")" | |
| KIND_CONFIG="${SCRIPT_DIR}/../manifests/kind.yaml" |
| - apiGroups: [""] | ||
| resources: [persistentvolumeclaims] | ||
| verbs: [create, delete, get, list] | ||
| - apiGroups: [""] | ||
| resources: [secrets] | ||
| verbs: [create, delete, get, list, update] |
There was a problem hiding this comment.
I think in the other manifests we usually unroll the arrays here:
This would also apply in all other places where this kind of structure is used on the PR 🤗
| const handleSave = () => { | ||
| const trimmedUser = user.trim(); | ||
| const trimmedGroups = groups.trim(); | ||
|
|
||
| if (!trimmedUser) { | ||
| setError('User is required.'); | ||
| return; | ||
| } | ||
|
|
||
| setError(null); | ||
| setDevAuth(trimmedUser, trimmedGroups); | ||
| notification.success('Auth headers saved.'); | ||
| }; |
There was a problem hiding this comment.
AI Review comment 🤖
(I can't verify / falsify it due to lack of frontend knowledge, however I thought I'd leave it here nonetheless)
UX: Input fields don't sync to trimmed values after save
handleSavetrims before callingsetDevAuth, but the React state (user, groups) still holds the original untrimmed string. If a user types admin (with spaces), the input will still show the padded value after saving even though admin is what's persisted and used.Fix: call
setUser(trimmedUser)andsetGroups(trimmedGroups)inhandleSaveafter saving.
| onChange={(_event, value) => { | ||
| setGroups(value); | ||
| setError(null); | ||
| }} |
There was a problem hiding this comment.
AI Review comment 🤖
(I can't verify / falsify it due to lack of frontend knowledge, however I thought I'd leave it here nonetheless)
UX: Error cleared on groups change, but error is only about user
onChangefor the groups field callssetError(null), so typing in groups clears "User is required" even though user is still empty. Either only clear the error in the user'sonChange, or use per-field error state.In
DebugAuthSection.tsx, the groupsonChangehandler callssetError(null):onChange={(_event, value) => { setGroups(value); setError(null); // ← clears the error }}But the only validation error that can be set is 'User is required.', which is triggered when the user field is empty. So if someone leaves the user field blank, hits Save (error appears), and then starts typing in the groups field — the error disappears even though user is still empty and would still fail validation on the next save attempt.
The fix is simply to remove
setError(null)from the groupsonChange, since groups has no validation of its own. Keep it only in the useronChange, where it's meaningful (typing in the user field is directly addressing the error).
thaorell
left a comment
There was a problem hiding this comment.
Thank you for tackling this, @andyatmiami
| const [user, setUser] = useState(() => getDevAuthUser()); | ||
| const [groups, setGroups] = useState(() => getDevAuthGroups()); | ||
| const [error, setError] = useState<string | null>(null); | ||
|
|
There was a problem hiding this comment.
Consider using useBrowserStorage hook like in WithValidImage.tsx to interact with browser's local storage. That could eliminate the need for the helper functions
ℹ️ NO GH ISSUE
Backend auth will soon be enabled by default. To support local development, contributors need a way to configure the kubeflow-userid and kubeflow-groups request headers from the frontend UI at runtime, without restarting the dev server.
This adds an Auth Headers section to the Debug page with User and Groups text fields that persist to localStorage. A dev-only Axios request interceptor reads the saved values and injects the corresponding headers into all API requests when running in development mode (APP_ENV=development).
Changes: