From 4e10118c3ad57e203df5983f37e6030875ad9941 Mon Sep 17 00:00:00 2001 From: linoman <2051016+linoman@users.noreply.github.com> Date: Thu, 20 Apr 2023 10:43:28 +0200 Subject: [PATCH] Add verbose settings (#65469) * Add `settings.CurrentVerbose` function * Add function to retrieve verbose settings * Add `/settings-verbose` endpoint * Restrict the verbose source types --- pkg/api/admin.go | 62 +++++++++++++++++++++++++++++++++++++++++ pkg/api/api.go | 1 + pkg/setting/provider.go | 21 ++++++++++++-- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/pkg/api/admin.go b/pkg/api/admin.go index e85ca94c572..38dc042ac6d 100644 --- a/pkg/api/admin.go +++ b/pkg/api/admin.go @@ -33,6 +33,19 @@ func (hs *HTTPServer) AdminGetSettings(c *contextmodel.ReqContext) response.Resp return response.JSON(http.StatusOK, settings) } +func (hs *HTTPServer) AdminGetVerboseSettings(c *contextmodel.ReqContext) response.Response { + bag := hs.SettingsProvider.CurrentVerbose() + if bag == nil { + return response.JSON(http.StatusNotImplemented, make(map[string]string)) + } + + verboseSettings, err := hs.getAuthorizedVerboseSettings(c.Req.Context(), c.SignedInUser, bag) + if err != nil { + return response.Error(http.StatusForbidden, "Failed to authorize settings", err) + } + return response.JSON(http.StatusOK, verboseSettings) +} + // swagger:route GET /admin/stats admin adminGetStats // // Fetch Grafana Stats. @@ -99,6 +112,55 @@ func (hs *HTTPServer) getAuthorizedSettings(ctx context.Context, user *user.Sign return authorizedBag, nil } +func (hs *HTTPServer) getAuthorizedVerboseSettings(ctx context.Context, user *user.SignedInUser, bag setting.VerboseSettingsBag) (setting.VerboseSettingsBag, error) { + if hs.AccessControl.IsDisabled() { + return bag, nil + } + + eval := func(scope string) (bool, error) { + return hs.AccessControl.Evaluate(ctx, user, ac.EvalPermission(ac.ActionSettingsRead, scope)) + } + + ok, err := eval(ac.ScopeSettingsAll) + if err != nil { + return nil, err + } + if ok { + return bag, nil + } + + authorizedBag := make(setting.VerboseSettingsBag) + + for section, keys := range bag { + ok, err := eval(ac.Scope("settings", section, "*")) + if err != nil { + return nil, err + } + if ok { + authorizedBag[section] = keys + continue + } + + for key := range keys { + ok, err := eval(ac.Scope("settings", section, key)) + if err != nil { + return nil, err + } + + if !ok { + continue + } + + if _, exists := authorizedBag[section]; !exists { + authorizedBag[section] = make(map[string]map[setting.VerboseSourceType]string) + } + authorizedBag[section][key] = bag[section][key] + } + } + + return authorizedBag, nil +} + // swagger:response adminGetSettingsResponse type GetSettingsResponse struct { // in:body diff --git a/pkg/api/api.go b/pkg/api/api.go index e93bc997e7f..8a2a093fe8a 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -594,6 +594,7 @@ func (hs *HTTPServer) registerRoutes() { // admin api r.Group("/api/admin", func(adminRoute routing.RouteRegister) { adminRoute.Get("/settings", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionSettingsRead)), routing.Wrap(hs.AdminGetSettings)) + adminRoute.Get("/settings-verbose", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionSettingsRead)), routing.Wrap(hs.AdminGetVerboseSettings)) adminRoute.Get("/stats", authorize(reqGrafanaAdmin, ac.EvalPermission(ac.ActionServerStatsRead)), routing.Wrap(hs.AdminGetStats)) adminRoute.Post("/pause-all-alerts", reqGrafanaAdmin, routing.Wrap(hs.PauseAllAlerts(setting.AlertingEnabled))) diff --git a/pkg/setting/provider.go b/pkg/setting/provider.go index 27d04beba58..81440b728fe 100644 --- a/pkg/setting/provider.go +++ b/pkg/setting/provider.go @@ -36,6 +36,9 @@ type Provider interface { // the current configured pairs of key/values for each // configuration section. Current() SettingsBag + + CurrentVerbose() VerboseSettingsBag + // Update receives a SettingsBag with the pairs of key/values // to be updated per section and a SettingsRemovals with the // section keys to be removed. @@ -92,6 +95,14 @@ type ReloadHandler interface { type SettingsBag map[string]map[string]string type SettingsRemovals map[string][]string +type VerboseSourceType string +type VerboseSettingsBag map[string]map[string]map[VerboseSourceType]string + +const ( + DB VerboseSourceType = "db" + System VerboseSourceType = "system" +) + func ProvideProvider(cfg *Cfg) *OSSImpl { return &OSSImpl{ Cfg: cfg, @@ -102,7 +113,7 @@ type OSSImpl struct { Cfg *Cfg } -func (o OSSImpl) Current() SettingsBag { +func (o *OSSImpl) Current() SettingsBag { settingsCopy := make(SettingsBag) for _, section := range o.Cfg.Raw.Sections() { @@ -115,6 +126,10 @@ func (o OSSImpl) Current() SettingsBag { return settingsCopy } +func (o *OSSImpl) CurrentVerbose() VerboseSettingsBag { + return nil +} + func (OSSImpl) Update(SettingsBag, SettingsRemovals) error { return errors.New("oss settings provider do not have support for settings updates") } @@ -127,9 +142,9 @@ func (o *OSSImpl) Section(section string) Section { return §ionImpl{section: o.Cfg.Raw.Section(section)} } -func (OSSImpl) RegisterReloadHandler(string, ReloadHandler) {} +func (*OSSImpl) RegisterReloadHandler(string, ReloadHandler) {} -func (o OSSImpl) IsFeatureToggleEnabled(name string) bool { +func (o *OSSImpl) IsFeatureToggleEnabled(name string) bool { return o.Cfg.IsFeatureToggleEnabled(name) }