From e0314bb864b326f6ab2b6c2ab75cab4b6bebc99e Mon Sep 17 00:00:00 2001 From: Gianluca Mardente Date: Mon, 6 Jul 2026 18:12:41 +0200 Subject: [PATCH] (bug) Fix panic and missing partial-access fallback on /events endpoints - A ClusterRole missing the `events` permission crashed the server: `getEvents`/`getEvent` called `c.AbortWithError(401, err)` with `err` already guaranteed nil at that point, which panics inside gin's `Context.Error()`. - Unlike other list endpoints (`/capiclusters`, etc.), `/events` had no fallback for users who can't list all EventTriggers. It hard-failed instead of degrading to the subset the user can individually `get`. - Two more instances of the same nil-`err` `AbortWithError` pattern: `getProfile` and a debug cluster/profile lookup handler. All fixed the same way. - `getEventTriggers` now takes `canListAll`/`user` and filters results down to only EventTriggers the caller can individually access via `canGetEventTrigger`, mirroring `GetManagedCAPIClusters`'s existing pattern. - `getEvent` (single EventTrigger lookup) now falls back to `canGetEventTrigger` when the caller can't list all EventTriggers, matching the same fallback already used for ClusterProfile/Profile lookups. --- internal/server/events.go | 12 ++++++++++-- internal/server/http.go | 26 ++++++++++++++------------ 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/internal/server/events.go b/internal/server/events.go index e94244b..160cc83 100644 --- a/internal/server/events.go +++ b/internal/server/events.go @@ -139,8 +139,9 @@ func getEventsInRange(eventTriggers []EventTrigger, limit, skip int) ([]EventTri return getSliceInRange(eventTriggers, limit, skip) } -// Return a list of existing EventTriggers -func (m *instance) getEventTriggers(ctx context.Context, +// Return a list of existing EventTriggers. If canListAll is false, the result is +// filtered down to only the EventTriggers user has explicit get access to. +func (m *instance) getEventTriggers(ctx context.Context, canListAll bool, user string, ) (map[corev1.ObjectReference]EventTriggerInfo, error) { eventTriggers := &eventv1beta1.EventTriggerList{} @@ -157,6 +158,13 @@ func (m *instance) getEventTriggers(ctx context.Context, continue } + if !canListAll { + ok, err := m.canGetEventTrigger(et.Name, user) + if err != nil || !ok { + continue + } + } + eventRef := corev1.ObjectReference{ Kind: eventv1beta1.EventTriggerKind, APIVersion: eventv1beta1.GroupVersion.String(), diff --git a/internal/server/http.go b/internal/server/http.go index 7ec2ea8..61af661 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -474,7 +474,7 @@ var ( } if !canGetResource { ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("user does not have permission to access resource. URI: %s", c.Request.URL)) - _ = c.AbortWithError(http.StatusUnauthorized, err) + _ = c.AbortWithError(http.StatusUnauthorized, errors.New("no permissions to access this resource")) return } @@ -538,13 +538,7 @@ var ( return } - if !canListEventTriggers { - ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("user does not have permission to access resource. URI: %s", c.Request.URL)) - _ = c.AbortWithError(http.StatusUnauthorized, err) - return - } - - eventTriggers, err := manager.getEventTriggers(c.Request.Context()) + eventTriggers, err := manager.getEventTriggers(c.Request.Context(), canListEventTriggers, user) if err != nil { ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("failed to get eventTriggers %s: %v", c.Request.URL, err)) _ = c.AbortWithError(http.StatusUnauthorized, err) @@ -599,9 +593,17 @@ var ( } if !canListEventTriggers { - ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("user does not have permission to access resource. URI: %s", c.Request.URL)) - _ = c.AbortWithError(http.StatusUnauthorized, err) - return + canGetEventTrigger, err := manager.canGetEventTrigger(eventTriggerName, user) + if err != nil { + ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("failed to verify permissions %s: %v", c.Request.URL, err)) + _ = c.AbortWithError(http.StatusUnauthorized, err) + return + } + if !canGetEventTrigger { + ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("user does not have permission to access resource. URI: %s", c.Request.URL)) + _ = c.AbortWithError(http.StatusUnauthorized, errors.New("no permissions to access this eventTrigger")) + return + } } canListAll, err := manager.canListCAPIClusters(user) @@ -740,7 +742,7 @@ var ( } if !canGetResource { ginLogger.V(logs.LogInfo).Info(fmt.Sprintf("user does not have permission to access resource. URI: %s", c.Request.URL)) - _ = c.AbortWithError(http.StatusUnauthorized, err) + _ = c.AbortWithError(http.StatusUnauthorized, errors.New("no permissions to access this resource")) return }