Identity: Add endpoint to get display info for an identifier (#91828)

This commit is contained in:
Ryan McKinley
2024-08-15 14:38:43 +03:00
committed by GitHub
parent c7fdf8ce70
commit a0cd89860e
67 changed files with 1535 additions and 282 deletions
+65 -15
View File
@@ -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
}
+150
View File
@@ -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)
})
}