AccessControl: Grant data source reader to all users when running oss (#49514)

* grant data source reader to all users when running oss or enterprise
without license

* fix asserts in alerting tests

* add oss licensing service for test setup

* fix tests to pass in enterprise

* lint

* fix tests

* set setting.IsEnterprise flag for tests

Co-authored-by: Yuriy Tseretyan <yuriy.tseretyan@grafana.com>
This commit is contained in:
Karl Persson
2022-05-25 13:43:58 +02:00
committed by GitHub
parent 8d313f54e2
commit 1796a1d277
4 changed files with 83 additions and 52 deletions

View File

@@ -94,6 +94,11 @@ func (hs *HTTPServer) declareFixedRoles() error {
Grants: []string{string(models.ROLE_ADMIN)}, Grants: []string{string(models.ROLE_ADMIN)},
} }
// when running oss or enterprise without a license all users should be able to query data sources
if !hs.License.FeatureEnabled("accesscontrol.enforcement") {
datasourcesReaderRole.Grants = []string{string(models.ROLE_VIEWER)}
}
datasourcesWriterRole := ac.RoleRegistration{ datasourcesWriterRole := ac.RoleRegistration{
Role: ac.RoleDTO{ Role: ac.RoleDTO{
Version: 3, Version: 3,
@@ -135,21 +140,6 @@ func (hs *HTTPServer) declareFixedRoles() error {
Grants: []string{string(models.ROLE_VIEWER)}, Grants: []string{string(models.ROLE_VIEWER)},
} }
datasourcesCompatibilityReaderRole := ac.RoleRegistration{
Role: ac.RoleDTO{
Version: 3,
Name: "fixed:datasources:compatibility:querier",
DisplayName: "Data source compatibility querier",
Description: "Only used for open source compatibility. Query data sources.",
Group: "Infrequently used",
Permissions: []ac.Permission{
{Action: datasources.ActionQuery},
{Action: datasources.ActionRead},
},
},
Grants: []string{string(models.ROLE_VIEWER)},
}
apikeyReaderRole := ac.RoleRegistration{ apikeyReaderRole := ac.RoleRegistration{
Role: ac.RoleDTO{ Role: ac.RoleDTO{
Version: 1, Version: 1,
@@ -419,8 +409,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
} }
return hs.AccessControl.DeclareFixedRoles( return hs.AccessControl.DeclareFixedRoles(
provisioningWriterRole, datasourcesReaderRole, datasourcesWriterRole, datasourcesIdReaderRole, provisioningWriterRole, datasourcesReaderRole, datasourcesWriterRole,
datasourcesCompatibilityReaderRole, orgReaderRole, orgWriterRole, datasourcesIdReaderRole, orgReaderRole, orgWriterRole,
orgMaintainerRole, teamsCreatorRole, teamsWriterRole, datasourcesExplorerRole, orgMaintainerRole, teamsCreatorRole, teamsWriterRole, datasourcesExplorerRole,
annotationsReaderRole, dashboardAnnotationsWriterRole, annotationsWriterRole, annotationsReaderRole, dashboardAnnotationsWriterRole, annotationsWriterRole,
dashboardsCreatorRole, dashboardsReaderRole, dashboardsWriterRole, dashboardsCreatorRole, dashboardsReaderRole, dashboardsWriterRole,

View File

@@ -34,6 +34,7 @@ import (
dashver "github.com/grafana/grafana/pkg/services/dashboardversion" dashver "github.com/grafana/grafana/pkg/services/dashboardversion"
"github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ldap" "github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/licensing"
"github.com/grafana/grafana/pkg/services/login/loginservice" "github.com/grafana/grafana/pkg/services/login/loginservice"
"github.com/grafana/grafana/pkg/services/login/logintest" "github.com/grafana/grafana/pkg/services/login/logintest"
"github.com/grafana/grafana/pkg/services/preference/preftest" "github.com/grafana/grafana/pkg/services/preference/preftest"
@@ -373,6 +374,7 @@ func setupHTTPServerWithCfgDb(t *testing.T, useFakeAccessControl, enableAccessCo
QuotaService: &quota.QuotaService{Cfg: cfg}, QuotaService: &quota.QuotaService{Cfg: cfg},
RouteRegister: routeRegister, RouteRegister: routeRegister,
SQLStore: store, SQLStore: store,
License: &licensing.OSSLicensingService{},
searchUsersService: searchusers.ProvideUsersService(db, filters.ProvideOSSSearchUserFilter()), searchUsersService: searchusers.ProvideUsersService(db, filters.ProvideOSSSearchUserFilter()),
dashboardService: dashboardservice.ProvideDashboardService( dashboardService: dashboardservice.ProvideDashboardService(
cfg, dashboardsStore, nil, features, cfg, dashboardsStore, nil, features,

View File

@@ -22,6 +22,7 @@ import (
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models" ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store" ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
"github.com/grafana/grafana/pkg/services/sqlstore" "github.com/grafana/grafana/pkg/services/sqlstore"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/tests/testinfra" "github.com/grafana/grafana/pkg/tests/testinfra"
) )
@@ -2279,9 +2280,9 @@ func TestEval(t *testing.T) {
testCases := []struct { testCases := []struct {
desc string desc string
payload string payload string
expectedStatusCode int expectedStatusCode func() int
expectedResponse string expectedResponse func() string
expectedMessage string expectedMessage func() string
}{ }{
{ {
desc: "alerting condition", desc: "alerting condition",
@@ -2307,8 +2308,10 @@ func TestEval(t *testing.T) {
} }
} }
`, `,
expectedStatusCode: http.StatusOK, expectedMessage: func() string { return "" },
expectedResponse: `{ expectedStatusCode: func() int { return http.StatusOK },
expectedResponse: func() string {
return `{
"instances": [ "instances": [
{ {
"schema": { "schema": {
@@ -2342,7 +2345,8 @@ func TestEval(t *testing.T) {
} }
} }
] ]
}`, }`
},
}, },
{ {
desc: "normal condition", desc: "normal condition",
@@ -2368,8 +2372,10 @@ func TestEval(t *testing.T) {
} }
} }
`, `,
expectedStatusCode: http.StatusOK, expectedMessage: func() string { return "" },
expectedResponse: `{ expectedStatusCode: func() int { return http.StatusOK },
expectedResponse: func() string {
return `{
"instances": [ "instances": [
{ {
"schema": { "schema": {
@@ -2403,7 +2409,8 @@ func TestEval(t *testing.T) {
} }
} }
] ]
}`, }`
},
}, },
{ {
desc: "condition not found in any query or expression", desc: "condition not found in any query or expression",
@@ -2429,8 +2436,11 @@ func TestEval(t *testing.T) {
} }
} }
`, `,
expectedStatusCode: http.StatusBadRequest, expectedStatusCode: func() int { return http.StatusBadRequest },
expectedMessage: "invalid condition: condition B not found in any query or expression: it should be one of: [A]", expectedMessage: func() string {
return "invalid condition: condition B not found in any query or expression: it should be one of: [A]"
},
expectedResponse: func() string { return "" },
}, },
{ {
desc: "unknown query datasource", desc: "unknown query datasource",
@@ -2454,8 +2464,19 @@ func TestEval(t *testing.T) {
} }
} }
`, `,
expectedStatusCode: http.StatusUnauthorized, expectedStatusCode: func() int {
expectedMessage: "user is not authorized to query one or many data sources used by the rule", if setting.IsEnterprise {
return http.StatusUnauthorized
}
return http.StatusBadRequest
},
expectedMessage: func() string {
if setting.IsEnterprise {
return "user is not authorized to query one or many data sources used by the rule"
}
return "invalid condition: invalid query A: data source not found: unknown"
},
expectedResponse: func() string { return "" },
}, },
} }
@@ -2476,12 +2497,12 @@ func TestEval(t *testing.T) {
err = json.Unmarshal(b, &res) err = json.Unmarshal(b, &res)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) assert.Equal(t, tc.expectedStatusCode(), resp.StatusCode)
if tc.expectedResponse != "" { if tc.expectedResponse() != "" {
require.JSONEq(t, tc.expectedResponse, string(b)) require.JSONEq(t, tc.expectedResponse(), string(b))
} }
if tc.expectedMessage != "" { if tc.expectedMessage() != "" {
assert.Equal(t, tc.expectedMessage, res.Message) assert.Equal(t, tc.expectedMessage(), res.Message)
assert.NotEmpty(t, res.TraceID) assert.NotEmpty(t, res.TraceID)
} }
}) })
@@ -2491,9 +2512,9 @@ func TestEval(t *testing.T) {
testCases = []struct { testCases = []struct {
desc string desc string
payload string payload string
expectedStatusCode int expectedStatusCode func() int
expectedResponse string expectedResponse func() string
expectedMessage string expectedMessage func() string
}{ }{
{ {
desc: "alerting condition", desc: "alerting condition",
@@ -2516,8 +2537,10 @@ func TestEval(t *testing.T) {
"now": "2021-04-11T14:38:14Z" "now": "2021-04-11T14:38:14Z"
} }
`, `,
expectedStatusCode: http.StatusOK, expectedMessage: func() string { return "" },
expectedResponse: `{ expectedStatusCode: func() int { return http.StatusOK },
expectedResponse: func() string {
return `{
"results": { "results": {
"A": { "A": {
"frames": [ "frames": [
@@ -2546,7 +2569,8 @@ func TestEval(t *testing.T) {
] ]
} }
} }
}`, }`
},
}, },
{ {
desc: "normal condition", desc: "normal condition",
@@ -2569,8 +2593,10 @@ func TestEval(t *testing.T) {
"now": "2021-04-11T14:38:14Z" "now": "2021-04-11T14:38:14Z"
} }
`, `,
expectedStatusCode: http.StatusOK, expectedMessage: func() string { return "" },
expectedResponse: `{ expectedStatusCode: func() int { return http.StatusOK },
expectedResponse: func() string {
return `{
"results": { "results": {
"A": { "A": {
"frames": [ "frames": [
@@ -2599,7 +2625,8 @@ func TestEval(t *testing.T) {
] ]
} }
} }
}`, }`
},
}, },
{ {
desc: "unknown query datasource", desc: "unknown query datasource",
@@ -2620,8 +2647,19 @@ func TestEval(t *testing.T) {
"now": "2021-04-11T14:38:14Z" "now": "2021-04-11T14:38:14Z"
} }
`, `,
expectedStatusCode: http.StatusUnauthorized, expectedResponse: func() string { return "" },
expectedMessage: "user is not authorized to query one or many data sources used by the rule", expectedStatusCode: func() int {
if setting.IsEnterprise {
return http.StatusUnauthorized
}
return http.StatusBadRequest
},
expectedMessage: func() string {
if setting.IsEnterprise {
return "user is not authorized to query one or many data sources used by the rule"
}
return "invalid queries or expressions: invalid query A: data source not found: unknown"
},
}, },
} }
@@ -2642,13 +2680,12 @@ func TestEval(t *testing.T) {
err = json.Unmarshal(b, &res) err = json.Unmarshal(b, &res)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, tc.expectedStatusCode, resp.StatusCode) assert.Equal(t, tc.expectedStatusCode(), resp.StatusCode)
if tc.expectedResponse != "" { if tc.expectedResponse() != "" {
require.JSONEq(t, tc.expectedResponse, string(b)) require.JSONEq(t, tc.expectedResponse(), string(b))
} }
if tc.expectedMessage() != "" {
if tc.expectedMessage != "" { require.Equal(t, tc.expectedMessage(), res.Message)
require.Equal(t, tc.expectedMessage, res.Message)
require.NotEmpty(t, res.TraceID) require.NotEmpty(t, res.TraceID)
} }
}) })

View File

@@ -17,6 +17,7 @@ import (
"gopkg.in/ini.v1" "gopkg.in/ini.v1"
"github.com/grafana/grafana/pkg/api" "github.com/grafana/grafana/pkg/api"
"github.com/grafana/grafana/pkg/extensions"
"github.com/grafana/grafana/pkg/infra/fs" "github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/models" "github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/server" "github.com/grafana/grafana/pkg/server"
@@ -35,6 +36,7 @@ func StartGrafanaEnv(t *testing.T, grafDir, cfgPath string) (string, *server.Tes
t.Helper() t.Helper()
ctx := context.Background() ctx := context.Background()
setting.IsEnterprise = extensions.IsEnterprise
listener, err := net.Listen("tcp", "127.0.0.1:0") listener, err := net.Listen("tcp", "127.0.0.1:0")
require.NoError(t, err) require.NoError(t, err)
cmdLineArgs := setting.CommandLineArgs{Config: cfgPath, HomePath: grafDir} cmdLineArgs := setting.CommandLineArgs{Config: cfgPath, HomePath: grafDir}