diff --git a/frontend/public/actions/features.ts b/frontend/public/actions/features.ts index 542e2f13b28..2c5d018873d 100644 --- a/frontend/public/actions/features.ts +++ b/frontend/public/actions/features.ts @@ -122,7 +122,9 @@ const ssarCheckActions = ssarChecks.map(({ flag, resourceAttributes, after }) => after(dispatch, allowed); } }, - (err) => handleError({ response: err.graphQLErrors[0]?.extensions }, flag, dispatch, fn), + (err) => { + handleError({ response: err.graphQLErrors[0]?.extensions }, flag, dispatch, fn); + }, ); return fn; }); diff --git a/frontend/public/actions/ui.ts b/frontend/public/actions/ui.ts index e64ea477fc7..d0a289a7f95 100644 --- a/frontend/public/actions/ui.ts +++ b/frontend/public/actions/ui.ts @@ -15,7 +15,7 @@ import { detectFeatures } from './features'; import { clearSSARFlags } from './flags'; import { OverviewSpecialGroup } from '../components/overview/constants'; import { setClusterID, setCreateProjectMessage, ActionType } from './common'; -import { subsClient } from '../graphql/client'; +import { subsClient, setForceHTTP } from '../graphql/client'; import { beginImpersonate, endImpersonate, @@ -241,15 +241,14 @@ export const startImpersonate = (kind: string, name: string, groups?: string[]) // This ensures flags refresh happens in sync with React's render cycle }; -// Action to refresh features after impersonation change -// Don't clear flags - just re-detect them. Old values remain until new ones are fetched. -// This prevents components from seeing PENDING state and showing loading spinners. export const refreshFeaturesAfterImpersonation = () => (dispatch) => { + setForceHTTP(true); dispatch(detectFeatures()); }; export const stopImpersonate = () => (dispatch) => { dispatch(endImpersonate()); subsClient.close(false, true); + setForceHTTP(false); dispatch(clearSSARFlags()); dispatch(detectFeatures()); }; diff --git a/frontend/public/graphql/client.ts b/frontend/public/graphql/client.ts index 6203d46d7f9..d331337af0a 100644 --- a/frontend/public/graphql/client.ts +++ b/frontend/public/graphql/client.ts @@ -12,6 +12,12 @@ import { URLQueryType, URLQueryVariables } from '../../@types/console/generated/ import { getConsoleRequestHeaders, coFetch } from '../co-fetch'; let wssErrors = 0; +// @ts-ignore TS6133 - forceHTTP is read inside the split() closure below +let forceHTTP = false; + +export const setForceHTTP = (force: boolean) => { + forceHTTP = force; +}; class GraphQLReady { private callback: VoidFunction; @@ -68,7 +74,7 @@ const wsLink = new WebSocketLink(subsClient); // fallback to http connection if websocket connection was not successful // iOS does not allow wss with self signed certificate -const link = split(() => wssErrors > 4, httpLink, wsLink); +const link = split(() => wssErrors > 4 || forceHTTP, httpLink, wsLink); const client = new ApolloClient({ link, diff --git a/pkg/server/server.go b/pkg/server/server.go index 728f0b4d6d0..0f69b38e725 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -381,9 +381,21 @@ func (s *Server) HTTPHandler() (http.Handler, error) { handler.InitPayload = resolver.InitPayload graphQLHandler := handler.NewHandlerFunc(schema, gql.NewHttpHandler(schema)) handle("/api/graphql", authHandlerWithUser(func(user *auth.User, w http.ResponseWriter, r *http.Request) { - ctx := context.WithValue(context.Background(), resolver.HeadersKey, map[string]interface{}{ + headers := map[string]interface{}{ "Authorization": fmt.Sprintf("Bearer %s", user.Token), - }) + } + if impUser := r.Header.Get("Impersonate-User"); impUser != "" { + headers["Impersonate-User"] = impUser + } + if consoleGroups := r.Header.Get("X-Console-Impersonate-Groups"); consoleGroups != "" { + groups := strings.Split(consoleGroups, ",") + groups = append(groups, "system:authenticated") + headers["Impersonate-Group"] = groups + } else if impGroups := r.Header.Values("Impersonate-Group"); len(impGroups) > 0 { + impGroups = append(impGroups, "system:authenticated") + headers["Impersonate-Group"] = impGroups + } + ctx := context.WithValue(r.Context(), resolver.HeadersKey, headers) graphQLHandler(w, r.WithContext(ctx)) }))