merge with main
This commit is contained in:
@@ -6,7 +6,6 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/api/response"
|
||||
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
|
||||
skv "github.com/grafana/grafana/pkg/services/secrets/kvstore"
|
||||
)
|
||||
|
||||
func (hs *HTTPServer) AdminRotateDataEncryptionKeys(c *contextmodel.ReqContext) response.Response {
|
||||
@@ -50,51 +49,3 @@ func (hs *HTTPServer) AdminRollbackSecrets(c *contextmodel.ReqContext) response.
|
||||
|
||||
return response.Respond(http.StatusOK, "Secrets rolled back successfully")
|
||||
}
|
||||
|
||||
// To migrate to the plugin, it must be installed and configured
|
||||
// so as not to lose access to migrated secrets
|
||||
func (hs *HTTPServer) AdminMigrateSecretsToPlugin(c *contextmodel.ReqContext) response.Response {
|
||||
if skv.EvaluateRemoteSecretsPlugin(c.Req.Context(), hs.secretsPluginManager, hs.Cfg) != nil {
|
||||
hs.log.Warn("Received secrets plugin migration request while plugin is not available")
|
||||
return response.Respond(http.StatusBadRequest, "Secrets plugin is not available")
|
||||
}
|
||||
err := hs.secretsPluginMigrator.TriggerPluginMigration(c.Req.Context(), true)
|
||||
if err != nil {
|
||||
hs.log.Error("Failed to trigger secret migration to plugin", "error", err.Error())
|
||||
return response.Respond(http.StatusInternalServerError, "Secret migration to plugin failed")
|
||||
}
|
||||
return response.Respond(http.StatusOK, "Secret migration to plugin triggered successfully")
|
||||
}
|
||||
|
||||
// To migrate from the plugin, it must be installed only
|
||||
// as it is possible the user disabled it and then wants to migrate
|
||||
func (hs *HTTPServer) AdminMigrateSecretsFromPlugin(c *contextmodel.ReqContext) response.Response {
|
||||
if hs.secretsPluginManager.SecretsManager(c.Req.Context()) == nil {
|
||||
hs.log.Warn("Received secrets plugin migration request while plugin is not installed")
|
||||
return response.Respond(http.StatusBadRequest, "Secrets plugin is not installed")
|
||||
}
|
||||
err := hs.secretsPluginMigrator.TriggerPluginMigration(c.Req.Context(), false)
|
||||
if err != nil {
|
||||
hs.log.Error("Failed to trigger secret migration from plugin", "error", err.Error())
|
||||
return response.Respond(http.StatusInternalServerError, "Secret migration from plugin failed")
|
||||
}
|
||||
return response.Respond(http.StatusOK, "Secret migration from plugin triggered successfully")
|
||||
}
|
||||
|
||||
func (hs *HTTPServer) AdminDeleteAllSecretsManagerPluginSecrets(c *contextmodel.ReqContext) response.Response {
|
||||
if hs.secretsPluginManager.SecretsManager(c.Req.Context()) == nil {
|
||||
hs.log.Warn("Received secrets plugin deletion request while plugin is not installed")
|
||||
return response.Respond(http.StatusBadRequest, "Secrets plugin is not installed")
|
||||
}
|
||||
items, err := hs.secretsStore.GetAll(c.Req.Context())
|
||||
if err != nil {
|
||||
return response.Respond(http.StatusInternalServerError, "an error occurred while retrieving secrets")
|
||||
}
|
||||
for _, item := range items {
|
||||
err := hs.secretsStore.Del(c.Req.Context(), *item.OrgId, *item.Namespace, *item.Type)
|
||||
if err != nil {
|
||||
return response.Respond(http.StatusInternalServerError, fmt.Sprintf("error deleting key with org=%v namespace=%v type=%v. error=%v", *item.OrgId, *item.Namespace, *item.Type, err.Error()))
|
||||
}
|
||||
}
|
||||
return response.Respond(http.StatusOK, fmt.Sprintf("All %d Secrets Manager plugin secrets deleted", len(items)))
|
||||
}
|
||||
|
||||
@@ -573,9 +573,6 @@ func (hs *HTTPServer) registerRoutes() {
|
||||
adminRoute.Post("/encryption/reencrypt-data-keys", reqGrafanaAdmin, routing.Wrap(hs.AdminReEncryptEncryptionKeys))
|
||||
adminRoute.Post("/encryption/reencrypt-secrets", reqGrafanaAdmin, routing.Wrap(hs.AdminReEncryptSecrets))
|
||||
adminRoute.Post("/encryption/rollback-secrets", reqGrafanaAdmin, routing.Wrap(hs.AdminRollbackSecrets))
|
||||
adminRoute.Post("/encryption/migrate-secrets/to-plugin", reqGrafanaAdmin, routing.Wrap(hs.AdminMigrateSecretsToPlugin))
|
||||
adminRoute.Post("/encryption/migrate-secrets/from-plugin", reqGrafanaAdmin, routing.Wrap(hs.AdminMigrateSecretsFromPlugin))
|
||||
adminRoute.Post("/encryption/delete-secretsmanagerplugin-secrets", reqGrafanaAdmin, routing.Wrap(hs.AdminDeleteAllSecretsManagerPluginSecrets))
|
||||
|
||||
adminRoute.Post("/provisioning/dashboards/reload", authorize(ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersDashboards)), routing.Wrap(hs.AdminProvisioningReloadDashboards))
|
||||
adminRoute.Post("/provisioning/plugins/reload", authorize(ac.EvalPermission(ActionProvisioningReload, ScopeProvisionersPlugins)), routing.Wrap(hs.AdminProvisioningReloadPlugins))
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
)
|
||||
|
||||
var datasourcesLogger = log.New("datasources")
|
||||
var secretsPluginError datasources.ErrDatasourceSecretsPluginUserFriendly
|
||||
|
||||
// swagger:route GET /datasources datasources getDataSources
|
||||
//
|
||||
@@ -178,9 +177,6 @@ func (hs *HTTPServer) DeleteDataSourceById(c *contextmodel.ReqContext) response.
|
||||
|
||||
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
|
||||
if err != nil {
|
||||
if errors.As(err, &secretsPluginError) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete datasource: "+err.Error(), err)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete datasource", err)
|
||||
}
|
||||
|
||||
@@ -257,9 +253,6 @@ func (hs *HTTPServer) DeleteDataSourceByUID(c *contextmodel.ReqContext) response
|
||||
|
||||
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
|
||||
if err != nil {
|
||||
if errors.As(err, &secretsPluginError) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete datasource: "+err.Error(), err)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete datasource", err)
|
||||
}
|
||||
|
||||
@@ -307,9 +300,6 @@ func (hs *HTTPServer) DeleteDataSourceByName(c *contextmodel.ReqContext) respons
|
||||
cmd := &datasources.DeleteDataSourceCommand{Name: name, OrgID: c.SignedInUser.GetOrgID()}
|
||||
err = hs.DataSourcesService.DeleteDataSource(c.Req.Context(), cmd)
|
||||
if err != nil {
|
||||
if errors.As(err, &secretsPluginError) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete datasource: "+err.Error(), err)
|
||||
}
|
||||
return response.Error(http.StatusInternalServerError, "Failed to delete datasource", err)
|
||||
}
|
||||
|
||||
@@ -405,10 +395,6 @@ func (hs *HTTPServer) AddDataSource(c *contextmodel.ReqContext) response.Respons
|
||||
return response.Error(http.StatusConflict, err.Error(), err)
|
||||
}
|
||||
|
||||
if errors.As(err, &secretsPluginError) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to add datasource: "+err.Error(), err)
|
||||
}
|
||||
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to add datasource", err)
|
||||
}
|
||||
|
||||
@@ -532,10 +518,6 @@ func (hs *HTTPServer) updateDataSourceByID(c *contextmodel.ReqContext, ds *datas
|
||||
return response.Error(http.StatusConflict, "Datasource has already been updated by someone else. Please reload and try again", err)
|
||||
}
|
||||
|
||||
if errors.As(err, &secretsPluginError) {
|
||||
return response.Error(http.StatusInternalServerError, "Failed to update datasource: "+err.Error(), err)
|
||||
}
|
||||
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Failed to update datasource", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -29,11 +29,6 @@ func (hs *HTTPServer) handleQueryMetricsError(err error) *response.NormalRespons
|
||||
return response.Error(http.StatusNotFound, "Data source not found", err)
|
||||
}
|
||||
|
||||
var secretsPlugin datasources.ErrDatasourceSecretsPluginUserFriendly
|
||||
if errors.As(err, &secretsPlugin) {
|
||||
return response.Error(http.StatusInternalServerError, fmt.Sprint("Secrets Plugin error: ", err.Error()), err)
|
||||
}
|
||||
|
||||
return response.ErrOrFallback(http.StatusInternalServerError, "Query data error", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
@@ -25,7 +24,6 @@ import (
|
||||
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginconfig"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
pluginSettings "github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings/service"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/query"
|
||||
@@ -40,11 +38,6 @@ type fakeDataSourceRequestValidator struct {
|
||||
err error
|
||||
}
|
||||
|
||||
type secretsErrorResponseBody struct {
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (rv *fakeDataSourceRequestValidator) Validate(ds *datasources.DataSource, req *http.Request) error {
|
||||
return rv.err
|
||||
}
|
||||
@@ -93,64 +86,6 @@ func TestAPIEndpoint_Metrics_QueryMetricsV2(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestAPIEndpoint_Metrics_PluginDecryptionFailure(t *testing.T) {
|
||||
cfg := setting.NewCfg()
|
||||
ds := &fakeDatasources.FakeDataSourceService{SimulatePluginFailure: true}
|
||||
db := &dbtest.FakeDB{ExpectedError: pluginsettings.ErrPluginSettingNotFound}
|
||||
pcp := plugincontext.ProvideService(cfg, localcache.ProvideService(),
|
||||
&pluginstore.FakePluginStore{
|
||||
PluginList: []pluginstore.Plugin{
|
||||
{
|
||||
JSONData: plugins.JSONData{
|
||||
ID: "grafana",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
&fakeDatasources.FakeCacheService{},
|
||||
ds, pluginSettings.ProvideService(db, secretstest.NewFakeSecretsService()), pluginconfig.NewFakePluginRequestConfigProvider(),
|
||||
)
|
||||
qds := query.ProvideService(
|
||||
cfg,
|
||||
nil,
|
||||
nil,
|
||||
&fakeDataSourceRequestValidator{},
|
||||
&fakePluginClient{
|
||||
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
||||
resp := backend.Responses{
|
||||
"A": backend.DataResponse{
|
||||
Error: errors.New("query failed"),
|
||||
},
|
||||
}
|
||||
return &backend.QueryDataResponse{Responses: resp}, nil
|
||||
},
|
||||
},
|
||||
pcp,
|
||||
)
|
||||
httpServer := SetupAPITestServer(t, func(hs *HTTPServer) {
|
||||
hs.queryDataService = qds
|
||||
hs.QuotaService = quotatest.New(false, nil)
|
||||
hs.pluginContextProvider = pcp
|
||||
})
|
||||
|
||||
t.Run("Status code is 500 and a secrets plugin error is returned if there is a problem getting secrets from the remote plugin", func(t *testing.T) {
|
||||
req := httpServer.NewPostRequest("/api/ds/query", strings.NewReader(reqValid))
|
||||
webtest.RequestWithSignedInUser(req, &user.SignedInUser{UserID: 1, OrgID: 1, Permissions: map[int64]map[string][]string{1: {datasources.ActionQuery: []string{datasources.ScopeAll}}}})
|
||||
resp, err := httpServer.SendJSON(req)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusInternalServerError, resp.StatusCode)
|
||||
buf := new(bytes.Buffer)
|
||||
_, err = buf.ReadFrom(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, resp.Body.Close())
|
||||
var resObj secretsErrorResponseBody
|
||||
err = json.Unmarshal(buf.Bytes(), &resObj)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, "", resObj.Error)
|
||||
require.Contains(t, resObj.Message, "Secrets Plugin error:")
|
||||
})
|
||||
}
|
||||
|
||||
var reqValid = `{
|
||||
"from": "",
|
||||
"to": "",
|
||||
|
||||
@@ -220,30 +220,29 @@ type FrontendSettingsDTO struct {
|
||||
|
||||
LicenseInfo FrontendSettingsLicenseInfoDTO `json:"licenseInfo"`
|
||||
|
||||
FeatureToggles map[string]bool `json:"featureToggles"`
|
||||
AnonymousEnabled bool `json:"anonymousEnabled"`
|
||||
AnonymousDeviceLimit int64 `json:"anonymousDeviceLimit"`
|
||||
RendererAvailable bool `json:"rendererAvailable"`
|
||||
RendererVersion string `json:"rendererVersion"`
|
||||
RendererDefaultImageWidth int `json:"rendererDefaultImageWidth"`
|
||||
RendererDefaultImageHeight int `json:"rendererDefaultImageHeight"`
|
||||
RendererDefaultImageScale float64 `json:"rendererDefaultImageScale"`
|
||||
SecretsManagerPluginEnabled bool `json:"secretsManagerPluginEnabled"`
|
||||
Http2Enabled bool `json:"http2Enabled"`
|
||||
GrafanaJavascriptAgent setting.GrafanaJavascriptAgent `json:"grafanaJavascriptAgent"`
|
||||
PluginCatalogURL string `json:"pluginCatalogURL"`
|
||||
PluginAdminEnabled bool `json:"pluginAdminEnabled"`
|
||||
PluginAdminExternalManageEnabled bool `json:"pluginAdminExternalManageEnabled"`
|
||||
PluginCatalogHiddenPlugins []string `json:"pluginCatalogHiddenPlugins"`
|
||||
PluginCatalogManagedPlugins []string `json:"pluginCatalogManagedPlugins"`
|
||||
PluginCatalogPreinstalledPlugins []setting.InstallPlugin `json:"pluginCatalogPreinstalledPlugins"`
|
||||
ExpressionsEnabled bool `json:"expressionsEnabled"`
|
||||
AwsAllowedAuthProviders []string `json:"awsAllowedAuthProviders"`
|
||||
AwsAssumeRoleEnabled bool `json:"awsAssumeRoleEnabled"`
|
||||
SupportBundlesEnabled bool `json:"supportBundlesEnabled"`
|
||||
SnapshotEnabled bool `json:"snapshotEnabled"`
|
||||
SecureSocksDSProxyEnabled bool `json:"secureSocksDSProxyEnabled"`
|
||||
ReportingStaticContext map[string]string `json:"reportingStaticContext"`
|
||||
FeatureToggles map[string]bool `json:"featureToggles"`
|
||||
AnonymousEnabled bool `json:"anonymousEnabled"`
|
||||
AnonymousDeviceLimit int64 `json:"anonymousDeviceLimit"`
|
||||
RendererAvailable bool `json:"rendererAvailable"`
|
||||
RendererVersion string `json:"rendererVersion"`
|
||||
RendererDefaultImageWidth int `json:"rendererDefaultImageWidth"`
|
||||
RendererDefaultImageHeight int `json:"rendererDefaultImageHeight"`
|
||||
RendererDefaultImageScale float64 `json:"rendererDefaultImageScale"`
|
||||
Http2Enabled bool `json:"http2Enabled"`
|
||||
GrafanaJavascriptAgent setting.GrafanaJavascriptAgent `json:"grafanaJavascriptAgent"`
|
||||
PluginCatalogURL string `json:"pluginCatalogURL"`
|
||||
PluginAdminEnabled bool `json:"pluginAdminEnabled"`
|
||||
PluginAdminExternalManageEnabled bool `json:"pluginAdminExternalManageEnabled"`
|
||||
PluginCatalogHiddenPlugins []string `json:"pluginCatalogHiddenPlugins"`
|
||||
PluginCatalogManagedPlugins []string `json:"pluginCatalogManagedPlugins"`
|
||||
PluginCatalogPreinstalledPlugins []setting.InstallPlugin `json:"pluginCatalogPreinstalledPlugins"`
|
||||
ExpressionsEnabled bool `json:"expressionsEnabled"`
|
||||
AwsAllowedAuthProviders []string `json:"awsAllowedAuthProviders"`
|
||||
AwsAssumeRoleEnabled bool `json:"awsAssumeRoleEnabled"`
|
||||
SupportBundlesEnabled bool `json:"supportBundlesEnabled"`
|
||||
SnapshotEnabled bool `json:"snapshotEnabled"`
|
||||
SecureSocksDSProxyEnabled bool `json:"secureSocksDSProxyEnabled"`
|
||||
ReportingStaticContext map[string]string `json:"reportingStaticContext"`
|
||||
PluginDependencies map[string][]plugins.Dependency `json:"pluginDependants"`
|
||||
|
||||
Azure FrontendSettingsAzureDTO `json:"azure"`
|
||||
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/licensing"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginsettings"
|
||||
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
|
||||
"github.com/grafana/grafana/pkg/services/secrets/kvstore"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/tsdb/grafanads"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@@ -174,7 +173,6 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
}
|
||||
|
||||
hasAccess := accesscontrol.HasAccess(hs.AccessControl, c)
|
||||
secretsManagerPluginEnabled := kvstore.EvaluateRemoteSecretsPlugin(c.Req.Context(), hs.secretsPluginManager, hs.Cfg) == nil
|
||||
trustedTypesDefaultPolicyEnabled := (hs.Cfg.CSPEnabled && strings.Contains(hs.Cfg.CSPTemplate, "require-trusted-types-for")) || (hs.Cfg.CSPReportOnlyEnabled && strings.Contains(hs.Cfg.CSPReportOnlyTemplate, "require-trusted-types-for"))
|
||||
isCloudMigrationTarget := hs.Features.IsEnabled(c.Req.Context(), featuremgmt.FlagOnPremToCloudMigrations) && hs.Cfg.CloudMigration.IsTarget
|
||||
featureToggles := hs.Features.GetEnabled(c.Req.Context())
|
||||
@@ -280,7 +278,6 @@ func (hs *HTTPServer) getFrontendSettings(c *contextmodel.ReqContext) (*dtos.Fro
|
||||
RendererDefaultImageWidth: hs.Cfg.RendererDefaultImageWidth,
|
||||
RendererDefaultImageHeight: hs.Cfg.RendererDefaultImageHeight,
|
||||
RendererDefaultImageScale: hs.Cfg.RendererDefaultImageScale,
|
||||
SecretsManagerPluginEnabled: secretsManagerPluginEnabled,
|
||||
Http2Enabled: hs.Cfg.Protocol == setting.HTTP2Scheme,
|
||||
GrafanaJavascriptAgent: hs.Cfg.GrafanaJavascriptAgent,
|
||||
PluginCatalogURL: hs.Cfg.PluginCatalogURL,
|
||||
|
||||
@@ -169,10 +169,9 @@ type HTTPServer struct {
|
||||
Listener net.Listener
|
||||
EncryptionService encryption.Internal
|
||||
SecretsService secrets.Service
|
||||
secretsPluginManager plugins.SecretsPluginManager
|
||||
secretsStore secretsKV.SecretsKVStore
|
||||
secretsMigrator secrets.Migrator
|
||||
secretsPluginMigrator spm.SecretMigrationProvider
|
||||
secretMigrationProvider spm.SecretMigrationProvider
|
||||
DataSourcesService datasources.DataSourceService
|
||||
cleanUpService *cleanup.CleanUpService
|
||||
tracer tracing.Tracer
|
||||
@@ -264,8 +263,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
dashboardPermissionsService accesscontrol.DashboardPermissionsService, dashboardVersionService dashver.Service,
|
||||
starService star.Service, csrfService csrf.Service, managedPlugins managedplugins.Manager,
|
||||
playlistService playlist.Service, apiKeyService apikey.Service, kvStore kvstore.KVStore,
|
||||
secretsMigrator secrets.Migrator, secretsPluginManager plugins.SecretsPluginManager, secretsService secrets.Service,
|
||||
secretsPluginMigrator spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
|
||||
secretsMigrator secrets.Migrator, secretsService secrets.Service,
|
||||
secretMigrationProvider spm.SecretMigrationProvider, secretsStore secretsKV.SecretsKVStore,
|
||||
publicDashboardsApi *publicdashboardsApi.Api, userService user.Service, tempUserService tempUser.Service,
|
||||
loginAttemptService loginAttempt.Service, orgService org.Service, orgDeletionService org.DeletionService, teamService team.Service,
|
||||
accesscontrolService accesscontrol.Service, navTreeService navtree.Service,
|
||||
@@ -329,9 +328,8 @@ func ProvideHTTPServer(opts ServerOptions, cfg *setting.Cfg, routeRegister routi
|
||||
SocialService: socialService,
|
||||
EncryptionService: encryptionService,
|
||||
SecretsService: secretsService,
|
||||
secretsPluginManager: secretsPluginManager,
|
||||
secretsMigrator: secretsMigrator,
|
||||
secretsPluginMigrator: secretsPluginMigrator,
|
||||
secretMigrationProvider: secretMigrationProvider,
|
||||
secretsStore: secretsStore,
|
||||
DataSourcesService: dataSourcesService,
|
||||
searchUsersService: searchUsersService,
|
||||
|
||||
Reference in New Issue
Block a user