AccessControl: Enable RBAC by default (#48813)
* Add RBAC section to settings * Default to RBAC enabled settings to true * Update tests to respect RBAC Co-authored-by: Karl Persson <kalle.persson@grafana.com>
This commit is contained in:
@@ -152,8 +152,8 @@ func TestAdminConfiguration_SendingToExternalAlertmanagers(t *testing.T) {
|
||||
|
||||
// Now, let's set an alert that should fire as quickly as possible.
|
||||
{
|
||||
// create the namespace we'll save our alerts to
|
||||
_, err := createFolder(t, s, 0, "default")
|
||||
// Create the namespace we'll save our alerts to
|
||||
err := createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
interval, err := model.ParseDuration("10s")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -16,10 +16,8 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/grafana/grafana/pkg/components/simplejson"
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
ngstore "github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
@@ -97,13 +95,13 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
desc: "un-authenticated request should fail",
|
||||
url: "http://%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusUnauthorized,
|
||||
expBody: `{"message": "Unauthorized"}`,
|
||||
expBody: `{"message":"Unauthorized"}`,
|
||||
},
|
||||
{
|
||||
desc: "viewer request should fail",
|
||||
url: "http://viewer:viewer@%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusForbidden,
|
||||
expBody: `{"message": "Permission denied"}`,
|
||||
expBody: `"title":"Access denied"`,
|
||||
},
|
||||
{
|
||||
desc: "editor request should succeed",
|
||||
@@ -132,7 +130,7 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
require.Equal(t, tc.expStatus, resp.StatusCode)
|
||||
b, err := ioutil.ReadAll(resp.Body)
|
||||
require.NoError(t, err)
|
||||
require.JSONEq(t, tc.expBody, string(b))
|
||||
require.Contains(t, string(b), tc.expBody)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -170,10 +168,10 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
expBody: `{"message": "Unauthorized"}`,
|
||||
},
|
||||
{
|
||||
desc: "viewer request should fail",
|
||||
desc: "viewer request should succeed",
|
||||
url: "http://viewer:viewer@%s/api/alertmanager/grafana/config/api/v1/alerts",
|
||||
expStatus: http.StatusForbidden,
|
||||
expBody: `{"message": "Permission denied"}`,
|
||||
expStatus: http.StatusOK,
|
||||
expBody: cfgBody,
|
||||
},
|
||||
{
|
||||
desc: "editor request should succeed",
|
||||
@@ -230,25 +228,25 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
desc: "un-authenticated request should fail",
|
||||
url: "http://%s/api/alertmanager/grafana/config/api/v2/silences",
|
||||
expStatus: http.StatusUnauthorized,
|
||||
expBody: `{"message": "Unauthorized"}`,
|
||||
expBody: `{"message":"Unauthorized"}`,
|
||||
},
|
||||
{
|
||||
desc: "viewer request should fail",
|
||||
url: "http://viewer:viewer@%s/api/alertmanager/grafana/api/v2/silences",
|
||||
expStatus: http.StatusForbidden,
|
||||
expBody: `{"message": "Permission denied"}`,
|
||||
expBody: `"title":"Access denied"`,
|
||||
},
|
||||
{
|
||||
desc: "editor request should succeed",
|
||||
url: "http://editor:editor@%s/api/alertmanager/grafana/api/v2/silences",
|
||||
expStatus: http.StatusAccepted,
|
||||
expBody: `{"id": "0", "message":"silence created"}`,
|
||||
expBody: `{"id":"0","message":"silence created"}`,
|
||||
},
|
||||
{
|
||||
desc: "admin request should succeed",
|
||||
url: "http://admin:admin@%s/api/alertmanager/grafana/api/v2/silences",
|
||||
expStatus: http.StatusAccepted,
|
||||
expBody: `{"id": "0", "message":"silence created"}`,
|
||||
expBody: `{"id":"0","message":"silence created"}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -269,7 +267,7 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
re := regexp.MustCompile(`"id":"([\w|-]+)"`)
|
||||
b = re.ReplaceAll(b, []byte(`"id":"0"`))
|
||||
}
|
||||
require.JSONEq(t, tc.expBody, string(b))
|
||||
require.Contains(t, string(b), tc.expBody)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -336,25 +334,25 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
desc: "un-authenticated request should fail",
|
||||
url: "http://%s/api/alertmanager/grafana/api/v2/silence/%s",
|
||||
expStatus: http.StatusUnauthorized,
|
||||
expBody: `{"message": "Unauthorized"}`,
|
||||
expBody: `{"message":"Unauthorized"}`,
|
||||
},
|
||||
{
|
||||
desc: "viewer request should fail",
|
||||
url: "http://viewer:viewer@%s/api/alertmanager/grafana/api/v2/silence/%s",
|
||||
expStatus: http.StatusForbidden,
|
||||
expBody: `{"message": "Permission denied"}`,
|
||||
expBody: `"title":"Access denied"`,
|
||||
},
|
||||
{
|
||||
desc: "editor request should succeed",
|
||||
url: "http://editor:editor@%s/api/alertmanager/grafana/api/v2/silence/%s",
|
||||
expStatus: http.StatusOK,
|
||||
expBody: `{"message": "silence deleted"}`,
|
||||
expBody: `{"message":"silence deleted"}`,
|
||||
},
|
||||
{
|
||||
desc: "admin request should succeed",
|
||||
url: "http://admin:admin@%s/api/alertmanager/grafana/api/v2/silence/%s",
|
||||
expStatus: http.StatusOK,
|
||||
expBody: `{"message": "silence deleted"}`,
|
||||
expBody: `{"message":"silence deleted"}`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -387,7 +385,7 @@ func TestAMConfigAccess(t *testing.T) {
|
||||
if tc.expStatus == http.StatusOK {
|
||||
unconsumedSilenceIdx++
|
||||
}
|
||||
require.JSONEq(t, tc.expBody, string(b))
|
||||
require.Contains(t, string(b), tc.expBody)
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -484,7 +482,8 @@ func TestAlertAndGroupsQuery(t *testing.T) {
|
||||
// Now, let's test the endpoint with some alerts.
|
||||
{
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err := createFolder(t, store, 0, "default")
|
||||
err := createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -578,10 +577,6 @@ func TestRulerAccess(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a users to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_VIEWER),
|
||||
@@ -599,36 +594,41 @@ func TestRulerAccess(t *testing.T) {
|
||||
Login: "admin",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "default", grafanaListedAddr, "editor", "editor")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "editor", "editor")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Now, let's test the access policies.
|
||||
testCases := []struct {
|
||||
desc string
|
||||
url string
|
||||
expStatus int
|
||||
expectedResponse string
|
||||
desc string
|
||||
url string
|
||||
expStatus int
|
||||
expectedMessage string
|
||||
}{
|
||||
{
|
||||
desc: "un-authenticated request should fail",
|
||||
url: "http://%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusUnauthorized,
|
||||
expectedResponse: `{"message": "Unauthorized"}`,
|
||||
desc: "un-authenticated request should fail",
|
||||
url: "http://%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusUnauthorized,
|
||||
expectedMessage: `Unauthorized`,
|
||||
},
|
||||
{
|
||||
desc: "viewer request should fail",
|
||||
url: "http://viewer:viewer@%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusForbidden,
|
||||
expectedResponse: `{"message": "Permission denied"}`,
|
||||
desc: "viewer request should fail",
|
||||
url: "http://viewer:viewer@%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusForbidden,
|
||||
expectedMessage: `You'll need additional permissions to perform this action. Permissions needed: any of alert.rules:update, alert.rules:create, alert.rules:delete`,
|
||||
},
|
||||
{
|
||||
desc: "editor request should succeed",
|
||||
url: "http://editor:editor@%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusAccepted,
|
||||
expectedResponse: `{"message":"rule group updated successfully"}`,
|
||||
desc: "editor request should succeed",
|
||||
url: "http://editor:editor@%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusAccepted,
|
||||
expectedMessage: `rule group updated successfully`,
|
||||
},
|
||||
{
|
||||
desc: "admin request should succeed",
|
||||
url: "http://admin:admin@%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusAccepted,
|
||||
expectedResponse: `{"message":"rule group updated successfully"}`,
|
||||
desc: "admin request should succeed",
|
||||
url: "http://admin:admin@%s/api/ruler/grafana/api/v1/rules/default",
|
||||
expStatus: http.StatusAccepted,
|
||||
expectedMessage: `rule group updated successfully`,
|
||||
},
|
||||
}
|
||||
|
||||
@@ -686,7 +686,10 @@ func TestRulerAccess(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tc.expStatus, resp.StatusCode)
|
||||
require.JSONEq(t, tc.expectedResponse, string(b))
|
||||
res := &Response{}
|
||||
err = json.Unmarshal(b, &res)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tc.expectedMessage, res.Message)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -706,10 +709,6 @@ func TestDeleteFolderWithRules(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
namespaceUID, err := createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_VIEWER),
|
||||
Password: "viewer",
|
||||
@@ -721,6 +720,12 @@ func TestDeleteFolderWithRules(t *testing.T) {
|
||||
Login: "editor",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
namespaceUID := "default"
|
||||
err = createFolder(t, namespaceUID, grafanaListedAddr, "editor", "editor")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "editor", "editor")
|
||||
require.NoError(t, err)
|
||||
|
||||
createRule(t, grafanaListedAddr, "default", "editor", "editor")
|
||||
|
||||
// First, let's have an editor create a rule within the folder/namespace.
|
||||
@@ -873,8 +878,9 @@ func TestAlertRuleCRUD(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "default")
|
||||
err = createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
interval, err := model.ParseDuration("1m")
|
||||
require.NoError(t, err)
|
||||
@@ -2011,10 +2017,6 @@ func TestQuota(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
@@ -2022,6 +2024,11 @@ func TestQuota(t *testing.T) {
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
interval, err := model.ParseDuration("1m")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -2265,7 +2272,7 @@ func TestEval(t *testing.T) {
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "default")
|
||||
err = createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
// test eval conditions
|
||||
@@ -2447,8 +2454,8 @@ func TestEval(t *testing.T) {
|
||||
}
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
expectedMessage: "invalid condition: invalid query A: data source not found: unknown",
|
||||
expectedStatusCode: http.StatusUnauthorized,
|
||||
expectedMessage: "user is not authorized to query one or many data sources used by the rule",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2613,8 +2620,8 @@ func TestEval(t *testing.T) {
|
||||
"now": "2021-04-11T14:38:14Z"
|
||||
}
|
||||
`,
|
||||
expectedStatusCode: http.StatusBadRequest,
|
||||
expectedMessage: "invalid queries or expressions: invalid query A: data source not found: unknown",
|
||||
expectedStatusCode: http.StatusUnauthorized,
|
||||
expectedMessage: "user is not authorized to query one or many data sources used by the rule",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -2650,25 +2657,20 @@ func TestEval(t *testing.T) {
|
||||
|
||||
// createFolder creates a folder for storing our alerts under. Grafana uses folders as a replacement for alert namespaces to match its permission model.
|
||||
// We use the dashboard command using IsFolder = true to tell it's a folder, it takes the dashboard as the name of the folder.
|
||||
func createFolder(t *testing.T, store *sqlstore.SQLStore, folderID int64, folderName string) (string, error) {
|
||||
func createFolder(t *testing.T, folderUID, grafanaListedAddr, login, password string) error {
|
||||
t.Helper()
|
||||
|
||||
cmd := models.SaveDashboardCommand{
|
||||
OrgId: 1, // default organisation
|
||||
FolderId: folderID,
|
||||
IsFolder: true,
|
||||
Dashboard: simplejson.NewFromAny(map[string]interface{}{
|
||||
"title": folderName,
|
||||
}),
|
||||
}
|
||||
dashboardsStore := database.ProvideDashboardStore(store)
|
||||
f, err := dashboardsStore.SaveDashboard(cmd)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return f.Uid, nil
|
||||
payload := fmt.Sprintf(`{"uid": "%s","title": "%s"}`, folderUID, folderUID)
|
||||
u := fmt.Sprintf("http://%s:%s@%s/api/folders", login, password, grafanaListedAddr)
|
||||
r := strings.NewReader(payload)
|
||||
// nolint:gosec
|
||||
resp, err := http.Post(u, "application/json", r)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, resp.Body.Close())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
return err
|
||||
}
|
||||
|
||||
// rulesNamespaceWithoutVariableValues takes a apimodels.NamespaceConfigResponse JSON-based input and makes the dynamic fields static e.g. uid, dates, etc.
|
||||
|
||||
@@ -775,8 +775,9 @@ func TestNotificationChannels(t *testing.T) {
|
||||
|
||||
{
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err := createFolder(t, env.SQLStore, 0, "default")
|
||||
err = createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
// Post the alertmanager config.
|
||||
u := fmt.Sprintf("http://grafana:password@%s/api/alertmanager/grafana/config/api/v1/alerts", grafanaListedAddr)
|
||||
|
||||
@@ -16,7 +16,9 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
dashboardsstore "github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol/resourcepermissions/types"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
@@ -35,10 +37,6 @@ func TestPrometheusRules(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
@@ -46,6 +44,11 @@ func TestPrometheusRules(t *testing.T) {
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
interval, err := model.ParseDuration("10s")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -330,10 +333,6 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
||||
dashboardUID, err := createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
@@ -341,6 +340,12 @@ func TestPrometheusRulesFilterByDashboard(t *testing.T) {
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
dashboardUID := "default"
|
||||
err = createFolder(t, dashboardUID, grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
interval, err := model.ParseDuration("10s")
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -625,23 +630,27 @@ func TestPrometheusRulesPermissions(t *testing.T) {
|
||||
})
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
dashboardsStore := dashboardsstore.ProvideDashboardStore(store)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
userID := createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Password: "password",
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
// Create a namespace under default organisation (orgID = 1) where we'll save some alerts.
|
||||
_, err = createFolder(t, store, 0, "folder1")
|
||||
// access control permissions store
|
||||
permissionsStore := acdb.ProvideService(store)
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "folder1", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create another namespace under default organisation (orgID = 1) where we'll save some alerts.
|
||||
_, err = createFolder(t, store, 0, "folder2")
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "folder2", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
// Create rule under folder1
|
||||
createRule(t, grafanaListedAddr, "folder1", "grafana", "password")
|
||||
|
||||
@@ -717,7 +726,8 @@ func TestPrometheusRulesPermissions(t *testing.T) {
|
||||
}
|
||||
|
||||
// remove permissions from folder2
|
||||
require.NoError(t, dashboardsStore.UpdateDashboardACL(context.Background(), 2, nil))
|
||||
removeFolderPermission(t, permissionsStore, 1, userID, models.ROLE_EDITOR, "folder2")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
// make sure that folder2 is not included in the response
|
||||
{
|
||||
@@ -764,8 +774,9 @@ func TestPrometheusRulesPermissions(t *testing.T) {
|
||||
}`, string(b))
|
||||
}
|
||||
|
||||
// remove permissions from _ALL_ folders
|
||||
require.NoError(t, dashboardsStore.UpdateDashboardACL(context.Background(), 1, nil))
|
||||
// remove permissions from folder1
|
||||
removeFolderPermission(t, permissionsStore, 1, userID, models.ROLE_EDITOR, "folder1")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
// make sure that no folders are included in the response
|
||||
{
|
||||
@@ -790,3 +801,42 @@ func TestPrometheusRulesPermissions(t *testing.T) {
|
||||
}`, string(b))
|
||||
}
|
||||
}
|
||||
|
||||
func reloadCachedPermissions(t *testing.T, addr, login, password string) {
|
||||
t.Helper()
|
||||
|
||||
u := fmt.Sprintf("http://%s:%s@%s/api/access-control/user/permissions?reloadcache=true", login, password, addr)
|
||||
// nolint:gosec
|
||||
resp, err := http.Get(u)
|
||||
t.Cleanup(func() {
|
||||
require.NoError(t, resp.Body.Close())
|
||||
})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, resp.StatusCode)
|
||||
}
|
||||
|
||||
func removeFolderPermission(t *testing.T, store *acdb.AccessControlStore, orgID, userID int64, role models.RoleType, uid string) {
|
||||
t.Helper()
|
||||
// remove user permissions on folder
|
||||
_, _ = store.SetUserResourcePermission(context.Background(), orgID, accesscontrol.User{ID: userID}, types.SetResourcePermissionCommand{
|
||||
Resource: "folders",
|
||||
ResourceID: uid,
|
||||
ResourceAttribute: "uid",
|
||||
}, nil)
|
||||
|
||||
// remove org role permissions from folder
|
||||
_, _ = store.SetBuiltInResourcePermission(context.Background(), orgID, string(role), types.SetResourcePermissionCommand{
|
||||
Resource: "folders",
|
||||
ResourceID: uid,
|
||||
ResourceAttribute: "uid",
|
||||
}, nil)
|
||||
|
||||
// remove org role children permissions from folder
|
||||
for _, c := range role.Children() {
|
||||
_, _ = store.SetBuiltInResourcePermission(context.Background(), orgID, string(c), types.SetResourcePermissionCommand{
|
||||
Resource: "folders",
|
||||
ResourceID: uid,
|
||||
ResourceAttribute: "uid",
|
||||
}, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package alerting
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
@@ -16,7 +15,7 @@ import (
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/tracing"
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
dashboardsstore "github.com/grafana/grafana/pkg/services/dashboards/database"
|
||||
acdb "github.com/grafana/grafana/pkg/services/accesscontrol/database"
|
||||
apimodels "github.com/grafana/grafana/pkg/services/ngalert/api/tooling/definitions"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
@@ -35,23 +34,25 @@ func TestAlertRulePermissions(t *testing.T) {
|
||||
})
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
dashboardsStore := dashboardsstore.ProvideDashboardStore(store)
|
||||
permissionsStore := acdb.ProvideService(store)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
userID := createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
Password: "password",
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "folder1")
|
||||
err = createFolder(t, "folder1", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = createFolder(t, store, 0, "folder2")
|
||||
err = createFolder(t, "folder2", grafanaListedAddr, "grafana", "password")
|
||||
// Create the namespace we'll save our alerts to.
|
||||
require.NoError(t, err)
|
||||
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
// Create rule under folder1
|
||||
createRule(t, grafanaListedAddr, "folder1", "grafana", "password")
|
||||
|
||||
@@ -180,7 +181,8 @@ func TestAlertRulePermissions(t *testing.T) {
|
||||
assert.JSONEq(t, expectedGetNamespaceResponseBody, body)
|
||||
|
||||
// remove permissions from folder2
|
||||
require.NoError(t, dashboardsStore.UpdateDashboardACL(context.Background(), 2, nil))
|
||||
removeFolderPermission(t, permissionsStore, 1, userID, models.ROLE_EDITOR, "folder2")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
// make sure that folder2 is not included in the response
|
||||
// nolint:gosec
|
||||
@@ -252,8 +254,9 @@ func TestAlertRulePermissions(t *testing.T) {
|
||||
assert.JSONEq(t, expectedGetNamespaceResponseBody, body)
|
||||
}
|
||||
|
||||
// Remove permissions from ALL folders.
|
||||
require.NoError(t, dashboardsStore.UpdateDashboardACL(context.Background(), 1, nil))
|
||||
// Remove permissions from folder1.
|
||||
removeFolderPermission(t, permissionsStore, 1, userID, models.ROLE_EDITOR, "folder1")
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
{
|
||||
u := fmt.Sprintf("http://grafana:password@%s/api/ruler/grafana/api/v1/rules", grafanaListedAddr)
|
||||
// nolint:gosec
|
||||
@@ -343,12 +346,6 @@ func TestAlertRuleConflictingTitle(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
_, err = createFolder(t, store, 0, "folder1")
|
||||
require.NoError(t, err)
|
||||
_, err = createFolder(t, store, 0, "folder2")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create user
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_ADMIN),
|
||||
@@ -356,6 +353,13 @@ func TestAlertRuleConflictingTitle(t *testing.T) {
|
||||
Login: "admin",
|
||||
})
|
||||
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "folder1", grafanaListedAddr, "admin", "admin")
|
||||
require.NoError(t, err)
|
||||
// Create the namespace we'll save our alerts to.
|
||||
err = createFolder(t, "folder2", grafanaListedAddr, "admin", "admin")
|
||||
require.NoError(t, err)
|
||||
|
||||
rules := newTestingRuleConfig(t)
|
||||
|
||||
buf := bytes.Buffer{}
|
||||
@@ -479,10 +483,6 @@ func TestRulerRulesFilterByDashboard(t *testing.T) {
|
||||
|
||||
grafanaListedAddr, store := testinfra.StartGrafana(t, dir, path)
|
||||
|
||||
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
||||
dashboardUID, err := createFolder(t, store, 0, "default")
|
||||
require.NoError(t, err)
|
||||
|
||||
// Create a user to make authenticated requests
|
||||
createUser(t, store, models.CreateUserCommand{
|
||||
DefaultOrgRole: string(models.ROLE_EDITOR),
|
||||
@@ -490,6 +490,12 @@ func TestRulerRulesFilterByDashboard(t *testing.T) {
|
||||
Login: "grafana",
|
||||
})
|
||||
|
||||
dashboardUID := "default"
|
||||
// Create the namespace under default organisation (orgID = 1) where we'll save our alerts to.
|
||||
err = createFolder(t, "default", grafanaListedAddr, "grafana", "password")
|
||||
require.NoError(t, err)
|
||||
reloadCachedPermissions(t, grafanaListedAddr, "grafana", "password")
|
||||
|
||||
interval, err := model.ParseDuration("10s")
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user