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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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)},
}
// 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{
Role: ac.RoleDTO{
Version: 3,
@ -135,21 +140,6 @@ func (hs *HTTPServer) declareFixedRoles() error {
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{
Role: ac.RoleDTO{
Version: 1,
@ -419,8 +409,8 @@ func (hs *HTTPServer) declareFixedRoles() error {
}
return hs.AccessControl.DeclareFixedRoles(
provisioningWriterRole, datasourcesReaderRole, datasourcesWriterRole, datasourcesIdReaderRole,
datasourcesCompatibilityReaderRole, orgReaderRole, orgWriterRole,
provisioningWriterRole, datasourcesReaderRole, datasourcesWriterRole,
datasourcesIdReaderRole, orgReaderRole, orgWriterRole,
orgMaintainerRole, teamsCreatorRole, teamsWriterRole, datasourcesExplorerRole,
annotationsReaderRole, dashboardAnnotationsWriterRole, annotationsWriterRole,
dashboardsCreatorRole, dashboardsReaderRole, dashboardsWriterRole,

View File

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

View File

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

View File

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