tests: add authorization tests for missing provisioning API endpoints
Add comprehensive authorization tests for: - Repository subresources (test, resources, history, status) - Connection status subresource - HistoricJobs resource - Settings and Stats resources All authorization paths are now covered by integration tests.
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
# Provisioning API Authorization Test Coverage Analysis
|
||||
|
||||
This document analyzes test coverage for all authorization paths in the provisioning API.
|
||||
|
||||
## Authorization Matrix
|
||||
|
||||
| Resource | Subresource | Verb | Fallback Role | Test File | Test Function | Coverage Status |
|
||||
|----------|-------------|------|---------------|-----------|---------------|-----------------|
|
||||
| **Repositories** | (CRUD) | create | Admin | `repository_test.go` | `TestIntegrationProvisioning_CreatingAndGetting` | ✅ Tested (viewer denied) |
|
||||
| Repositories | (CRUD) | read | Admin | `repository_test.go` | `TestIntegrationProvisioning_CreatingAndGetting` | ✅ Tested (viewer denied) |
|
||||
| Repositories | (CRUD) | update | Admin | `repository_test.go` | Various | ✅ Tested (implicitly) |
|
||||
| Repositories | (CRUD) | delete | Admin | `repository_test.go` | `TestIntegrationProvisioning_DeleteRepositoryAndReleaseResources` | ✅ Tested |
|
||||
| Repositories | test | POST | Admin | `repository_subresources_auth_test.go` | `TestIntegrationProvisioning_RepositorySubresourcesAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
| Repositories | files | GET | Any auth (route) | `files_test.go` | `TestIntegrationProvisioning_FilesAuthorization` | ✅ Tested (viewer/editor/admin) |
|
||||
| Repositories | files | POST | Any auth (route) | `files_test.go` | `TestIntegrationProvisioning_FilesAuthorization` | ✅ Tested (viewer/editor/admin) |
|
||||
| Repositories | files | PUT | Any auth (route) | `files_test.go` | `TestIntegrationProvisioning_FilesAuthorization` | ✅ Tested (viewer/editor/admin) |
|
||||
| Repositories | files | DELETE | Any auth (route) | `files_test.go` | `TestIntegrationProvisioning_FilesAuthorization` | ✅ Tested (viewer/editor/admin) |
|
||||
| Repositories | refs | GET | Editor | `repository_test.go` | `TestIntegrationProvisioning_RefsPermissions` | ✅ Tested (editor allowed, viewer denied) |
|
||||
| Repositories | resources | GET | Admin | `repository_subresources_auth_test.go` | `TestIntegrationProvisioning_RepositorySubresourcesAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
| Repositories | history | GET | Admin | `repository_subresources_auth_test.go` | `TestIntegrationProvisioning_RepositorySubresourcesAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
| Repositories | status | GET | Admin | `repository_subresources_auth_test.go` | `TestIntegrationProvisioning_RepositorySubresourcesAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
| Repositories | jobs | POST | Editor | `repository_test.go` | `TestIntegrationProvisioning_JobPermissions` | ✅ Tested (editor allowed, viewer denied) |
|
||||
| Repositories | jobs | GET | Editor | `repository_test.go` | `TestIntegrationProvisioning_JobPermissions` | ✅ Tested (implicitly) |
|
||||
| **Connections** | (CRUD) | create | Admin | `connection_test.go` | `TestIntegrationProvisioning_ConnectionCRUDL` | ✅ Tested (viewer denied) |
|
||||
| Connections | (CRUD) | read | Admin | `connection_test.go` | `TestIntegrationProvisioning_ConnectionCRUDL` | ✅ Tested (viewer denied) |
|
||||
| Connections | (CRUD) | update | Admin | `connection_test.go` | `TestIntegrationProvisioning_ConnectionCRUDL` | ✅ Tested |
|
||||
| Connections | (CRUD) | delete | Admin | `connection_test.go` | `TestIntegrationProvisioning_ConnectionCRUDL` | ✅ Tested |
|
||||
| Connections | status | GET | Admin | `connection_status_auth_test.go` | `TestIntegrationProvisioning_ConnectionStatusAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
| **Jobs** | (CRUD) | create | Editor | `repository_test.go` | `TestIntegrationProvisioning_JobPermissions` | ✅ Tested (editor allowed, viewer denied) |
|
||||
| Jobs | (CRUD) | read | Editor | Various job tests | Various | ✅ Tested (implicitly) |
|
||||
| Jobs | (CRUD) | update | Editor | Various job tests | Various | ✅ Tested (implicitly) |
|
||||
| Jobs | (CRUD) | delete | Editor | `deletejob_test.go` | Various | ✅ Tested (implicitly) |
|
||||
| **HistoricJobs** | - | GET | Admin | `historicjobs_auth_test.go` | `TestIntegrationProvisioning_HistoricJobsAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
| **Settings** | - | GET | Viewer | `settings_stats_auth_test.go` | `TestIntegrationProvisioning_SettingsAuthorization` | ✅ Tested (viewer/editor/admin all allowed) |
|
||||
| **Stats** | - | GET | Admin | `settings_stats_auth_test.go` | `TestIntegrationProvisioning_StatsAuthorization` | ✅ Tested (admin allowed, editor/viewer denied) |
|
||||
|
||||
## Test Coverage Status
|
||||
|
||||
✅ **All authorization paths are now covered by tests!**
|
||||
|
||||
### Recently Added Tests
|
||||
|
||||
1. **Repositories/test subresource** (POST) - ✅ Added in `repository_subresources_auth_test.go`
|
||||
2. **Repositories/resources subresource** (GET) - ✅ Added in `repository_subresources_auth_test.go`
|
||||
3. **Repositories/history subresource** (GET) - ✅ Added in `repository_subresources_auth_test.go`
|
||||
4. **Repositories/status subresource** (GET) - ✅ Added in `repository_subresources_auth_test.go`
|
||||
5. **Connections/status subresource** (GET) - ✅ Added in `connection_status_auth_test.go`
|
||||
6. **HistoricJobs resource** (GET) - ✅ Added in `historicjobs_auth_test.go`
|
||||
7. **Settings resource** (GET) - ✅ Completed in `settings_stats_auth_test.go`
|
||||
8. **Stats resource** (GET) - ✅ Completed in `settings_stats_auth_test.go`
|
||||
|
||||
## Test Patterns
|
||||
|
||||
### Successful Authorization Test Pattern
|
||||
```go
|
||||
t.Run("editor can GET refs", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("refs").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "editor should be able to GET refs")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
```
|
||||
|
||||
### Denied Authorization Test Pattern
|
||||
```go
|
||||
t.Run("viewer cannot GET refs", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("refs").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET refs")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
```
|
||||
|
||||
## Test Files
|
||||
|
||||
- `repository_subresources_auth_test.go` - Tests for test, resources, history, status subresources
|
||||
- `connection_status_auth_test.go` - Tests for connection status subresource
|
||||
- `historicjobs_auth_test.go` - Tests for HistoricJobs resource
|
||||
- `settings_stats_auth_test.go` - Tests for Settings and Stats resources
|
||||
- `repository_test.go` - Tests for repository CRUD, refs, jobs
|
||||
- `connection_test.go` - Tests for connection CRUD
|
||||
- `files_test.go` - Tests for files subresource authorization
|
||||
|
||||
## Notes
|
||||
|
||||
- Files subresource authorization is tested at the route level (any authenticated user) and at the resource level (via DualReadWriter)
|
||||
- Jobs subresource is well covered with explicit editor/viewer/admin tests
|
||||
- Repository CRUD operations are covered with viewer denial tests
|
||||
- Connection CRUD operations are covered with viewer denial tests
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
func TestIntegrationProvisioning_ConnectionStatusAuthorization(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
createOptions := metav1.CreateOptions{FieldValidation: "Strict"}
|
||||
|
||||
// Create a connection for testing
|
||||
connection := &unstructured.Unstructured{Object: map[string]any{
|
||||
"apiVersion": "provisioning.grafana.app/v0alpha1",
|
||||
"kind": "Connection",
|
||||
"metadata": map[string]any{
|
||||
"name": "connection-status-test",
|
||||
"namespace": "default",
|
||||
},
|
||||
"spec": map[string]any{
|
||||
"type": "github",
|
||||
"github": map[string]any{
|
||||
"appID": "123456",
|
||||
"installationID": "454545",
|
||||
},
|
||||
},
|
||||
"secure": map[string]any{
|
||||
"privateKey": map[string]any{
|
||||
"create": "someSecret",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
_, err := helper.Connections.Resource.Create(ctx, connection, createOptions)
|
||||
require.NoError(t, err, "failed to create connection")
|
||||
|
||||
t.Run("admin can GET connection status", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("connections").
|
||||
Name("connection-status-test").
|
||||
SubResource("status").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to GET connection status")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor cannot GET connection status", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("connections").
|
||||
Name("connection-status-test").
|
||||
SubResource("status").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to GET connection status")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot GET connection status", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("connections").
|
||||
Name("connection-status-test").
|
||||
SubResource("status").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET connection status")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
provisioning "github.com/grafana/grafana/apps/provisioning/pkg/apis/provisioning/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
func TestIntegrationProvisioning_HistoricJobsAuthorization(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
const repo = "historicjobs-auth-test"
|
||||
testRepo := TestRepo{
|
||||
Name: repo,
|
||||
Target: "folder",
|
||||
Copies: map[string]string{}, // No files needed for this test
|
||||
ExpectedDashboards: 0,
|
||||
ExpectedFolders: 1, // Repository creates a folder
|
||||
}
|
||||
helper.CreateRepo(t, testRepo)
|
||||
|
||||
// Trigger a job to create a historic job entry
|
||||
jobSpec := provisioning.JobSpec{
|
||||
Action: provisioning.JobActionPull,
|
||||
Pull: &provisioning.SyncJobOptions{},
|
||||
}
|
||||
body := asJSON(jobSpec)
|
||||
|
||||
// Create a job as admin
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("jobs").
|
||||
Body(body).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
require.NoError(t, result.Error(), "should be able to create job")
|
||||
require.Equal(t, http.StatusAccepted, statusCode)
|
||||
|
||||
// Wait for job to complete and become historic
|
||||
helper.AwaitJobs(t, repo)
|
||||
historicJob := helper.AwaitLatestHistoricJob(t, repo)
|
||||
require.NotNil(t, historicJob, "should have a historic job")
|
||||
|
||||
historicJobName := historicJob.GetName()
|
||||
|
||||
t.Run("admin can GET historic job", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("historicjobs").
|
||||
Name(historicJobName).
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to GET historic job")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor cannot GET historic job", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("historicjobs").
|
||||
Name(historicJobName).
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to GET historic job")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot GET historic job", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("historicjobs").
|
||||
Name(historicJobName).
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET historic job")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,237 @@
|
||||
package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
func TestIntegrationProvisioning_RepositorySubresourcesAuthorization(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
const repo = "subresources-auth-test"
|
||||
testRepo := TestRepo{
|
||||
Name: repo,
|
||||
Target: "folder",
|
||||
Copies: map[string]string{}, // No files needed for this test
|
||||
ExpectedDashboards: 0,
|
||||
ExpectedFolders: 1, // Repository creates a folder
|
||||
}
|
||||
helper.CreateRepo(t, testRepo)
|
||||
|
||||
t.Run("test subresource", func(t *testing.T) {
|
||||
newRepoConfig := map[string]any{
|
||||
"apiVersion": "provisioning.grafana.app/v0alpha1",
|
||||
"kind": "Repository",
|
||||
"spec": map[string]any{
|
||||
"title": "Test Configuration",
|
||||
"type": "local",
|
||||
"local": map[string]any{
|
||||
"path": helper.ProvisioningPath,
|
||||
},
|
||||
"workflows": []string{"write"},
|
||||
"sync": map[string]any{
|
||||
"enabled": true,
|
||||
"target": "folder",
|
||||
"intervalSeconds": 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
configBytes, err := json.Marshal(newRepoConfig)
|
||||
require.NoError(t, err)
|
||||
|
||||
t.Run("admin can POST test", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name("test-config-auth").
|
||||
SubResource("test").
|
||||
Body(configBytes).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to POST test")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor cannot POST test", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name("test-config-auth").
|
||||
SubResource("test").
|
||||
Body(configBytes).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to POST test")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot POST test", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Post().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name("test-config-auth").
|
||||
SubResource("test").
|
||||
Body(configBytes).
|
||||
SetHeader("Content-Type", "application/json").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to POST test")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("resources subresource", func(t *testing.T) {
|
||||
t.Run("admin can GET resources", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("resources").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to GET resources")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor cannot GET resources", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("resources").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to GET resources")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot GET resources", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("resources").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET resources")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("history subresource", func(t *testing.T) {
|
||||
t.Run("admin can GET history (or BadRequest if not supported)", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("history").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
// Admin should pass authorization - may get BadRequest if repo doesn't support history
|
||||
// but should NOT get Forbidden (which would indicate authorization failure)
|
||||
if result.Error() != nil {
|
||||
require.False(t, apierrors.IsForbidden(result.Error()), "admin should not get Forbidden error")
|
||||
// Local repos don't support history, so BadRequest is expected
|
||||
require.True(t, apierrors.IsBadRequest(result.Error()) || statusCode == http.StatusBadRequest,
|
||||
"should get BadRequest if history not supported, not Forbidden")
|
||||
} else {
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK if history is supported")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("editor cannot GET history", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("history").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to GET history")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot GET history", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("history").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET history")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("status subresource", func(t *testing.T) {
|
||||
t.Run("admin can GET status", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("status").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to GET status")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor cannot GET status", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("status").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to GET status")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot GET status", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("repositories").
|
||||
Name(repo).
|
||||
SubResource("status").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET status")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
package provisioning
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util/testutil"
|
||||
)
|
||||
|
||||
func TestIntegrationProvisioning_SettingsAuthorization(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
t.Run("viewer can GET settings", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("settings").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "viewer should be able to GET settings")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor can GET settings", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("settings").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "editor should be able to GET settings")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("admin can GET settings", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("settings").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to GET settings")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntegrationProvisioning_StatsAuthorization(t *testing.T) {
|
||||
testutil.SkipIntegrationTestInShortMode(t)
|
||||
|
||||
helper := runGrafana(t)
|
||||
ctx := context.Background()
|
||||
|
||||
// Create a repository to ensure stats endpoint has data
|
||||
const repo = "stats-auth-test"
|
||||
helper.CreateRepo(t, TestRepo{
|
||||
Name: repo,
|
||||
Target: "folder",
|
||||
Copies: map[string]string{},
|
||||
ExpectedDashboards: 0,
|
||||
ExpectedFolders: 1,
|
||||
})
|
||||
|
||||
t.Run("admin can GET stats", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.AdminREST.Get().
|
||||
Namespace("default").
|
||||
Resource("stats").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.NoError(t, result.Error(), "admin should be able to GET stats")
|
||||
require.Equal(t, http.StatusOK, statusCode, "should return 200 OK")
|
||||
})
|
||||
|
||||
t.Run("editor cannot GET stats", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.EditorREST.Get().
|
||||
Namespace("default").
|
||||
Resource("stats").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "editor should not be able to GET stats")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
|
||||
t.Run("viewer cannot GET stats", func(t *testing.T) {
|
||||
var statusCode int
|
||||
result := helper.ViewerREST.Get().
|
||||
Namespace("default").
|
||||
Resource("stats").
|
||||
Do(ctx).StatusCode(&statusCode)
|
||||
|
||||
require.Error(t, result.Error(), "viewer should not be able to GET stats")
|
||||
require.Equal(t, http.StatusForbidden, statusCode, "should return 403 Forbidden")
|
||||
require.True(t, apierrors.IsForbidden(result.Error()), "error should be forbidden")
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user