mirror of
https://github.com/grafana/grafana.git
synced 2025-07-29 21:52:14 +08:00
342 lines
9.8 KiB
Go
342 lines
9.8 KiB
Go
package identity
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
|
|
"github.com/grafana/grafana/pkg/apiserver/rest"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/tests/apis"
|
|
"github.com/grafana/grafana/pkg/tests/testinfra"
|
|
"github.com/grafana/grafana/pkg/tests/testsuite"
|
|
)
|
|
|
|
var gvrTeams = schema.GroupVersionResource{
|
|
Group: "iam.grafana.app",
|
|
Version: "v0alpha1",
|
|
Resource: "teams",
|
|
}
|
|
|
|
var gvrUsers = schema.GroupVersionResource{
|
|
Group: "iam.grafana.app",
|
|
Version: "v0alpha1",
|
|
Resource: "users",
|
|
}
|
|
|
|
func TestMain(m *testing.M) {
|
|
testsuite.Run(m)
|
|
}
|
|
|
|
func TestIntegrationIdentity(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
|
AppModeProduction: false, // required for experimental APIs
|
|
DisableAnonymous: true,
|
|
EnableFeatureToggles: []string{
|
|
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the example service
|
|
},
|
|
})
|
|
_, err := helper.NewDiscoveryClient().ServerResourcesForGroupVersion("iam.grafana.app/v0alpha1")
|
|
require.NoError(t, err)
|
|
|
|
t.Run("read only views", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
teamClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
|
User: helper.Org1.Admin,
|
|
GVR: gvrTeams,
|
|
})
|
|
rsp, err := teamClient.Resource.List(ctx, metav1.ListOptions{})
|
|
require.NoError(t, err)
|
|
found := teamClient.SanitizeJSONList(rsp, "name", "labels")
|
|
require.JSONEq(t, `{
|
|
"items": [
|
|
{
|
|
"apiVersion": "iam.grafana.app/v0alpha1",
|
|
"kind": "Team",
|
|
"metadata": {
|
|
"creationTimestamp": "${creationTimestamp}",
|
|
"name": "${name}",
|
|
"namespace": "default",
|
|
"resourceVersion": "${resourceVersion}"
|
|
},
|
|
"spec": {
|
|
"email": "staff@Org1",
|
|
"title": "staff"
|
|
},
|
|
"status": {}
|
|
}
|
|
]
|
|
}`, found)
|
|
|
|
// Org1 users
|
|
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
|
User: helper.Org1.Admin,
|
|
GVR: gvrUsers,
|
|
})
|
|
rsp, err = userClient.Resource.List(ctx, metav1.ListOptions{})
|
|
require.NoError(t, err)
|
|
|
|
// Get just the specs (avoids values that change with each deployment)
|
|
found = teamClient.SpecJSON(rsp)
|
|
require.JSONEq(t, `[
|
|
{
|
|
"disabled": false,
|
|
"email": "admin@localhost",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": true,
|
|
"login": "admin",
|
|
"name": "",
|
|
"provisioned": false
|
|
},
|
|
{
|
|
"disabled": false,
|
|
"email": "admin2-1",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": true,
|
|
"login": "admin2-1",
|
|
"name": "",
|
|
"provisioned": false
|
|
},
|
|
{
|
|
"disabled": false,
|
|
"email": "editor-1",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": false,
|
|
"login": "editor-1",
|
|
"name": "",
|
|
"provisioned": false
|
|
},
|
|
{
|
|
"disabled": false,
|
|
"email": "viewer-1",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": false,
|
|
"login": "viewer-1",
|
|
"name": "",
|
|
"provisioned": false
|
|
}
|
|
]`, found)
|
|
|
|
// OrgB users
|
|
userClient = helper.GetResourceClient(apis.ResourceClientArgs{
|
|
User: helper.Org1.Admin, // super admin
|
|
Namespace: helper.Namespacer(helper.OrgB.Admin.Identity.GetOrgID()), // list values for orgB with super admin user
|
|
GVR: gvrUsers,
|
|
})
|
|
rsp, err = userClient.Resource.List(ctx, metav1.ListOptions{})
|
|
require.NoError(t, err)
|
|
|
|
// Get just the specs (avoids values that change with each deployment)
|
|
found = teamClient.SpecJSON(rsp)
|
|
require.JSONEq(t, `[
|
|
{
|
|
"disabled": false,
|
|
"email": "admin2-1",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": true,
|
|
"login": "admin2-1",
|
|
"name": "",
|
|
"provisioned": false
|
|
},
|
|
{
|
|
"disabled": false,
|
|
"email": "admin2-2",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": false,
|
|
"login": "admin2-2",
|
|
"name": "",
|
|
"provisioned": false
|
|
},
|
|
{
|
|
"disabled": false,
|
|
"email": "editor-2",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": false,
|
|
"login": "editor-2",
|
|
"name": "",
|
|
"provisioned": false
|
|
},
|
|
{
|
|
"disabled": false,
|
|
"email": "viewer-2",
|
|
"emailVerified": false,
|
|
"grafanaAdmin": false,
|
|
"login": "viewer-2",
|
|
"name": "",
|
|
"provisioned": false
|
|
}
|
|
]`, found)
|
|
})
|
|
}
|
|
|
|
func TestIntegrationUsers(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
// TODO: Figure out why rest.Mode4 is failing
|
|
modes := []rest.DualWriterMode{rest.Mode0, rest.Mode1, rest.Mode2, rest.Mode3}
|
|
for _, mode := range modes {
|
|
t.Run(fmt.Sprintf("User CRUD operations with dual writer mode %d", mode), func(t *testing.T) {
|
|
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
|
AppModeProduction: false,
|
|
DisableAnonymous: true,
|
|
APIServerStorageType: "unified",
|
|
UnifiedStorageConfig: map[string]setting.UnifiedStorageConfig{
|
|
"users.iam.grafana.app": {
|
|
DualWriterMode: mode,
|
|
},
|
|
},
|
|
EnableFeatureToggles: []string{
|
|
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs,
|
|
featuremgmt.FlagKubernetesAuthnMutation,
|
|
},
|
|
})
|
|
doUserCRUDTestsUsingTheNewAPIs(t, helper)
|
|
|
|
if mode < 3 {
|
|
doUserCRUDTestsUsingTheLegacyAPIs(t, helper)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func doUserCRUDTestsUsingTheNewAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
|
t.Run("should create user and delete it using the new APIs", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
|
|
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
|
User: helper.Org1.Admin,
|
|
GVR: gvrUsers,
|
|
})
|
|
|
|
// Create the user
|
|
created, err := userClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml"), metav1.CreateOptions{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, created)
|
|
|
|
// Verify creation response
|
|
createdSpec := created.Object["spec"].(map[string]interface{})
|
|
require.Equal(t, "testuser1@example123.com", createdSpec["email"])
|
|
require.Equal(t, "testuser1", createdSpec["login"])
|
|
require.Equal(t, "Test User 1", createdSpec["name"])
|
|
require.Equal(t, false, createdSpec["provisioned"])
|
|
|
|
// Get the UID from created user for fetching
|
|
createdUID := created.GetName()
|
|
require.NotEmpty(t, createdUID)
|
|
|
|
_, err = userClient.Resource.List(ctx, metav1.ListOptions{})
|
|
require.NoError(t, err)
|
|
|
|
fetched, err := userClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, fetched)
|
|
|
|
// Verify fetched user matches created user
|
|
fetchedSpec := fetched.Object["spec"].(map[string]interface{})
|
|
require.Equal(t, "testuser1@example123.com", fetchedSpec["email"])
|
|
require.Equal(t, "testuser1", fetchedSpec["login"])
|
|
require.Equal(t, "Test User 1", fetchedSpec["name"])
|
|
require.Equal(t, false, fetchedSpec["provisioned"])
|
|
|
|
// Verify metadata
|
|
require.Equal(t, createdUID, fetched.GetName())
|
|
require.Equal(t, "default", fetched.GetNamespace())
|
|
|
|
err = userClient.Resource.Delete(ctx, createdUID, metav1.DeleteOptions{})
|
|
require.NoError(t, err)
|
|
|
|
// Verify deletion
|
|
_, err = userClient.Resource.Get(ctx, createdUID, metav1.GetOptions{})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "not found")
|
|
})
|
|
|
|
t.Run("should not be able to create user when using a user with insufficient permissions", func(t *testing.T) {
|
|
for _, user := range []apis.User{
|
|
helper.Org1.Editor,
|
|
helper.Org1.Viewer,
|
|
} {
|
|
t.Run(fmt.Sprintf("with basic role: %s", user.Identity.GetOrgRole()), func(t *testing.T) {
|
|
ctx := context.Background()
|
|
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
|
User: user,
|
|
GVR: gvrUsers,
|
|
})
|
|
|
|
// Create the user
|
|
_, err := userClient.Resource.Create(ctx, helper.LoadYAMLOrJSONFile("testdata/user-test-create-v0.yaml"), metav1.CreateOptions{})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "unauthorized request")
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func doUserCRUDTestsUsingTheLegacyAPIs(t *testing.T, helper *apis.K8sTestHelper) {
|
|
t.Run("should create user using legacy APIs and delete it using the new APIs", func(t *testing.T) {
|
|
ctx := context.Background()
|
|
userClient := helper.GetResourceClient(apis.ResourceClientArgs{
|
|
User: helper.Org1.Admin,
|
|
GVR: gvrUsers,
|
|
})
|
|
|
|
legacyUserPayload := `{
|
|
"name": "Test User 2",
|
|
"email": "testuser2@example.com",
|
|
"login": "testuser2",
|
|
"password": "password123"
|
|
}`
|
|
|
|
rsp := apis.DoRequest(helper, apis.RequestParams{
|
|
User: helper.Org1.Admin,
|
|
Method: "POST",
|
|
Path: "/api/admin/users",
|
|
Body: []byte(legacyUserPayload),
|
|
}, &user.User{})
|
|
|
|
require.NotNil(t, rsp)
|
|
require.Equal(t, 200, rsp.Response.StatusCode)
|
|
require.NotEmpty(t, rsp.Result.UID)
|
|
|
|
// Now try to fetch the user via the new API
|
|
user, err := userClient.Resource.Get(context.Background(), rsp.Result.UID, metav1.GetOptions{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, user)
|
|
|
|
// Verify fetched user matches created user
|
|
userSpec := user.Object["spec"].(map[string]interface{})
|
|
require.Equal(t, "testuser2@example.com", userSpec["email"])
|
|
require.Equal(t, "testuser2", userSpec["login"])
|
|
require.Equal(t, "Test User 2", userSpec["name"])
|
|
require.Equal(t, false, userSpec["provisioned"])
|
|
|
|
// Verify metadata
|
|
require.Equal(t, rsp.Result.UID, user.GetName())
|
|
require.Equal(t, "default", user.GetNamespace())
|
|
|
|
// Now delete the user using the legacy API
|
|
deleteRsp := apis.DoRequest(helper, apis.RequestParams{
|
|
User: helper.Org1.Admin,
|
|
Method: "DELETE",
|
|
Path: fmt.Sprintf("/api/admin/users/%d", rsp.Result.ID),
|
|
}, &apis.AnyResource{})
|
|
require.Equal(t, 200, deleteRsp.Response.StatusCode)
|
|
|
|
// Verify deletion
|
|
_, err = userClient.Resource.Get(ctx, rsp.Result.UID, metav1.GetOptions{})
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "not found")
|
|
})
|
|
}
|