Identity: Add endpoint to get display info for an identifier (#91828)
This commit is contained in:
+65
-15
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
||||
"github.com/grafana/grafana/pkg/services/quota/quotaimpl"
|
||||
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
|
||||
"github.com/grafana/grafana/pkg/services/team"
|
||||
"github.com/grafana/grafana/pkg/services/team/teamimpl"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
||||
@@ -49,7 +50,7 @@ const Org1 = "Org1"
|
||||
type K8sTestHelper struct {
|
||||
t *testing.T
|
||||
env server.TestEnv
|
||||
namespacer request.NamespaceMapper
|
||||
Namespacer request.NamespaceMapper
|
||||
|
||||
Org1 OrgUsers // default
|
||||
OrgB OrgUsers // some other id
|
||||
@@ -66,7 +67,7 @@ func NewK8sTestHelper(t *testing.T, opts testinfra.GrafanaOpts) *K8sTestHelper {
|
||||
c := &K8sTestHelper{
|
||||
env: *env,
|
||||
t: t,
|
||||
namespacer: request.GetNamespaceMapper(nil),
|
||||
Namespacer: request.GetNamespaceMapper(nil),
|
||||
}
|
||||
|
||||
c.Org1 = c.createTestUsers(Org1)
|
||||
@@ -120,7 +121,7 @@ func (c *K8sTestHelper) GetResourceClient(args ResourceClientArgs) *K8sResourceC
|
||||
c.t.Helper()
|
||||
|
||||
if args.Namespace == "" {
|
||||
args.Namespace = c.namespacer(args.User.Identity.GetOrgID())
|
||||
args.Namespace = c.Namespacer(args.User.Identity.GetOrgID())
|
||||
}
|
||||
|
||||
client, err := dynamic.NewForConfig(args.User.NewRestConfig())
|
||||
@@ -147,8 +148,46 @@ func (c *K8sTestHelper) AsStatusError(err error) *errors.StatusError {
|
||||
return statusError
|
||||
}
|
||||
|
||||
func (c *K8sResourceClient) SanitizeJSONList(v *unstructured.UnstructuredList, replaceMeta ...string) string {
|
||||
c.t.Helper()
|
||||
|
||||
clean := &unstructured.UnstructuredList{}
|
||||
for _, item := range v.Items {
|
||||
copy := c.sanitizeObject(&item, replaceMeta...)
|
||||
clean.Items = append(clean.Items, *copy)
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(clean, "", " ")
|
||||
require.NoError(c.t, err)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
func (c *K8sResourceClient) SpecJSON(v *unstructured.UnstructuredList) string {
|
||||
c.t.Helper()
|
||||
|
||||
clean := []any{}
|
||||
for _, item := range v.Items {
|
||||
clean = append(clean, item.Object["spec"])
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(clean, "", " ")
|
||||
require.NoError(c.t, err)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// remove the meta keys that are expected to change each time
|
||||
func (c *K8sResourceClient) SanitizeJSON(v *unstructured.Unstructured) string {
|
||||
func (c *K8sResourceClient) SanitizeJSON(v *unstructured.Unstructured, replaceMeta ...string) string {
|
||||
c.t.Helper()
|
||||
copy := c.sanitizeObject(v)
|
||||
|
||||
out, err := json.MarshalIndent(copy, "", " ")
|
||||
// fmt.Printf("%s", out)
|
||||
require.NoError(c.t, err)
|
||||
return string(out)
|
||||
}
|
||||
|
||||
// remove the meta keys that are expected to change each time
|
||||
func (c *K8sResourceClient) sanitizeObject(v *unstructured.Unstructured, replaceMeta ...string) *unstructured.Unstructured {
|
||||
c.t.Helper()
|
||||
|
||||
deep := v.DeepCopy()
|
||||
@@ -170,24 +209,24 @@ func (c *K8sResourceClient) SanitizeJSON(v *unstructured.Unstructured) string {
|
||||
meta, ok := copy["metadata"].(map[string]any)
|
||||
require.True(c.t, ok)
|
||||
|
||||
replaceMeta := []string{"creationTimestamp", "resourceVersion", "uid"}
|
||||
replaceMeta = append(replaceMeta, "creationTimestamp", "resourceVersion", "uid")
|
||||
for _, key := range replaceMeta {
|
||||
old, ok := meta[key]
|
||||
require.True(c.t, ok)
|
||||
require.NotEmpty(c.t, old)
|
||||
meta[key] = fmt.Sprintf("${%s}", key)
|
||||
if ok {
|
||||
require.NotEmpty(c.t, old)
|
||||
meta[key] = fmt.Sprintf("${%s}", key)
|
||||
}
|
||||
}
|
||||
|
||||
out, err := json.MarshalIndent(copy, "", " ")
|
||||
// fmt.Printf("%s", out)
|
||||
require.NoError(c.t, err)
|
||||
return string(out)
|
||||
return deep
|
||||
}
|
||||
|
||||
type OrgUsers struct {
|
||||
Admin User
|
||||
Editor User
|
||||
Viewer User
|
||||
|
||||
// The team with admin+editor in it (but not viewer)
|
||||
Staff team.Team
|
||||
}
|
||||
|
||||
type User struct {
|
||||
@@ -243,7 +282,7 @@ func (c *K8sTestHelper) PostResource(user User, resource string, payload AnyReso
|
||||
|
||||
namespace := payload.Namespace
|
||||
if namespace == "" {
|
||||
namespace = c.namespacer(user.Identity.GetOrgID())
|
||||
namespace = c.Namespacer(user.Identity.GetOrgID())
|
||||
}
|
||||
|
||||
path := fmt.Sprintf("/apis/%s/namespaces/%s/%s",
|
||||
@@ -383,11 +422,14 @@ func (c *K8sTestHelper) LoadYAMLOrJSON(body string) *unstructured.Unstructured {
|
||||
|
||||
func (c *K8sTestHelper) createTestUsers(orgName string) OrgUsers {
|
||||
c.t.Helper()
|
||||
return OrgUsers{
|
||||
users := OrgUsers{
|
||||
Admin: c.CreateUser("admin", orgName, org.RoleAdmin, nil),
|
||||
Editor: c.CreateUser("editor", orgName, org.RoleEditor, nil),
|
||||
Viewer: c.CreateUser("viewer", orgName, org.RoleViewer, nil),
|
||||
}
|
||||
users.Staff = c.CreateTeam("staff", "staff@"+orgName, users.Admin.Identity.GetOrgID())
|
||||
// TODO add admin and editor to staff
|
||||
return users
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) CreateUser(name string, orgName string, basicRole org.RoleType, permissions []resourcepermissions.SetResourcePermissionCommand) User {
|
||||
@@ -542,3 +584,11 @@ func (c *K8sTestHelper) CreateDS(cmd *datasources.AddDataSourceCommand) *datasou
|
||||
require.NoError(c.t, err)
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func (c *K8sTestHelper) CreateTeam(name, email string, orgID int64) team.Team {
|
||||
c.t.Helper()
|
||||
|
||||
team, err := c.env.Server.HTTPServer.TeamService.CreateTeam(context.Background(), name, email, orgID)
|
||||
require.NoError(c.t, err)
|
||||
return team
|
||||
}
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
||||
"github.com/grafana/grafana/pkg/tests/apis"
|
||||
"github.com/grafana/grafana/pkg/tests/testinfra"
|
||||
"github.com/grafana/grafana/pkg/tests/testsuite"
|
||||
"github.com/stretchr/testify/require"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
)
|
||||
|
||||
var gvrTeams = schema.GroupVersionResource{
|
||||
Group: "identity.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
Resource: "teams",
|
||||
}
|
||||
|
||||
var gvrUsers = schema.GroupVersionResource{
|
||||
Group: "identity.grafana.app",
|
||||
Version: "v0alpha1",
|
||||
Resource: "users",
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
testsuite.Run(m)
|
||||
}
|
||||
|
||||
func TestIntegrationRequiresDevMode(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("skipping integration test")
|
||||
}
|
||||
helper := apis.NewK8sTestHelper(t, testinfra.GrafanaOpts{
|
||||
AppModeProduction: true, // should fail
|
||||
DisableAnonymous: true,
|
||||
EnableFeatureToggles: []string{
|
||||
featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs, // Required to start the example service
|
||||
},
|
||||
})
|
||||
|
||||
_, err := helper.NewDiscoveryClient().ServerResourcesForGroupVersion("identity.grafana.app/v0alpha1")
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
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("identity.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")
|
||||
// fmt.Printf("%s", found)
|
||||
require.JSONEq(t, `{
|
||||
"items": [
|
||||
{
|
||||
"apiVersion": "identity.grafana.app/v0alpha1",
|
||||
"kind": "Team",
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"grafana.app/originName": "SQL",
|
||||
"grafana.app/originPath": "${originPath}"
|
||||
},
|
||||
"creationTimestamp": "${creationTimestamp}",
|
||||
"name": "${name}",
|
||||
"namespace": "default",
|
||||
"resourceVersion": "${resourceVersion}"
|
||||
},
|
||||
"spec": {
|
||||
"email": "staff@Org1",
|
||||
"name": "staff"
|
||||
}
|
||||
}
|
||||
]
|
||||
}`, 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)
|
||||
// fmt.Printf("%s", found) // NOTE the first value does not have an email or login
|
||||
require.JSONEq(t, `[
|
||||
{},
|
||||
{
|
||||
"email": "admin-1",
|
||||
"login": "admin-1"
|
||||
},
|
||||
{
|
||||
"email": "editor-1",
|
||||
"login": "editor-1"
|
||||
},
|
||||
{
|
||||
"email": "viewer-1",
|
||||
"login": "viewer-1"
|
||||
}
|
||||
]`, 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)
|
||||
fmt.Printf("%s", found) // NOTE the first value does not have an email or login
|
||||
require.JSONEq(t, `[
|
||||
{
|
||||
"email": "admin-3",
|
||||
"login": "admin-3"
|
||||
},
|
||||
{
|
||||
"email": "editor-3",
|
||||
"login": "editor-3"
|
||||
},
|
||||
{
|
||||
"email": "viewer-3",
|
||||
"login": "viewer-3"
|
||||
}
|
||||
]`, found)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user