From bd0a81f3327ccb059497ee9b302a5be44b897f9c Mon Sep 17 00:00:00 2001 From: Amir Raminfar Date: Sat, 14 Dec 2024 13:12:43 -0800 Subject: [PATCH] fix: fixes missed cases to validate user scope when downloading, streaming and actions (#3460) --- assets/auto-imports.d.ts | 15 ++++++++++ internal/web/actions.go | 11 +++++++ internal/web/actions_test.go | 2 +- internal/web/logs.go | 57 ++++++++++++++++++++++++++++++++++-- 4 files changed, 82 insertions(+), 3 deletions(-) diff --git a/assets/auto-imports.d.ts b/assets/auto-imports.d.ts index 964038f4..d089f7ab 100644 --- a/assets/auto-imports.d.ts +++ b/assets/auto-imports.d.ts @@ -369,6 +369,21 @@ declare global { // @ts-ignore export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' import('vue') + // @ts-ignore + export type { DrawerWidth } from './composable/drawer' + import('./composable/drawer') + // @ts-ignore + export type { LogStreamSource } from './composable/eventStreams' + import('./composable/eventStreams') + // @ts-ignore + export type { Config, Profile } from './stores/config' + import('./stores/config') + // @ts-ignore + export type { Host } from './stores/hosts' + import('./stores/hosts') + // @ts-ignore + export type { Settings } from './stores/settings' + import('./stores/settings') } // for vue template auto import diff --git a/internal/web/actions.go b/internal/web/actions.go index 1d1c0186..23aefcdc 100644 --- a/internal/web/actions.go +++ b/internal/web/actions.go @@ -14,6 +14,17 @@ func (h *handler) containerActions(w http.ResponseWriter, r *http.Request) { action := chi.URLParam(r, "action") id := chi.URLParam(r, "id") + validIdMap, err := h.validContainerIDsForHost(r, hostKey(r)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if _, ok := validIdMap[id]; !ok { + http.Error(w, "container not found", http.StatusUnauthorized) + return + } + containerService, err := h.multiHostService.FindContainer(hostKey(r), id) if err != nil { log.Error().Err(err).Msg("error while trying to find container") diff --git a/internal/web/actions_test.go b/internal/web/actions_test.go index 9f980040..f76bdac8 100644 --- a/internal/web/actions_test.go +++ b/internal/web/actions_test.go @@ -76,7 +76,7 @@ func Test_handler_containerActions_unknown_container(t *testing.T) { rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) - assert.Equal(t, 404, rr.Code) + assert.Equal(t, 401, rr.Code) } func Test_handler_containerActions_start(t *testing.T) { diff --git a/internal/web/logs.go b/internal/web/logs.go index 445731ee..46c9cd9a 100644 --- a/internal/web/logs.go +++ b/internal/web/logs.go @@ -26,12 +26,46 @@ import ( "github.com/docker/docker/pkg/stdcopy" "github.com/dustin/go-humanize" "github.com/go-chi/chi/v5" + "github.com/samber/lo" "github.com/rs/zerolog/log" ) +func (h *handler) validContainerIDsForHost(r *http.Request, host string) (map[string]docker.Container, error) { + usersFilter := h.config.Filter + if h.config.Authorization.Provider != NONE { + user := auth.UserFromContext(r.Context()) + if user.ContainerFilter.Exists() { + usersFilter = user.ContainerFilter + } + } + + validContainers, err := h.multiHostService.ListContainersForHost(host, usersFilter) + if err != nil { + return nil, err + } + + validIdMap := lo.KeyBy(validContainers, func(item docker.Container) string { + return item.ID + }) + + return validIdMap, nil +} + func (h *handler) downloadLogs(w http.ResponseWriter, r *http.Request) { id := chi.URLParam(r, "id") + + validIdMap, err := h.validContainerIDsForHost(r, hostKey(r)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if _, ok := validIdMap[id]; !ok { + http.Error(w, "container not found", http.StatusUnauthorized) + return + } + containerService, err := h.multiHostService.FindContainer(hostKey(r), id) if err != nil { http.Error(w, err.Error(), http.StatusBadRequest) @@ -103,6 +137,17 @@ func (h *handler) fetchLogsBetweenDates(w http.ResponseWriter, r *http.Request) return } + validIdMap, err := h.validContainerIDsForHost(r, hostKey(r)) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + if _, ok := validIdMap[id]; !ok { + http.Error(w, "container not found", http.StatusUnauthorized) + return + } + containerService, err := h.multiHostService.FindContainer(hostKey(r), id) if err != nil { http.Error(w, err.Error(), http.StatusNotFound) @@ -420,8 +465,16 @@ loop: } sseWriter.Message(logEvent) case container := <-newContainers: - events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-started", Host: container.Host} - go streamLogs(container) + validIdMap, err := h.validContainerIDsForHost(r, container.Host) + if err != nil { + log.Error().Err(err).Msg("error fetching valid container IDs") + continue + } + + if _, ok := validIdMap[container.ID]; ok { + events <- &docker.ContainerEvent{ActorID: container.ID, Name: "container-started", Host: container.Host} + go streamLogs(container) + } case event := <-events: log.Debug().Str("event", event.Name).Str("container", event.ActorID).Msg("received event")