IAM: Add teams/{id}/groups as a custom endpoint to Teams API (#114228)
* Add teams/{id}/groups as a custom endpoint
* TeamGroupsHandler OSS and Ent registration
* Update OpenAPI spec
* Add indexer tests for external group mapping
* Remove noopsearch
* Remove unnecessary interface declaration, fixes
* Chores
* fmt
* Rename constant
* Align the rest to the changes of main
* Update workspace
* Add missing file
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
package externalgroupmapping
|
||||
|
||||
import "k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
type TeamGroupsHandler interface {
|
||||
rest.Storage
|
||||
rest.Scoper
|
||||
rest.StorageMetadata
|
||||
rest.Connecter
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package externalgroupmapping
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
iamv0alpha1 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
)
|
||||
|
||||
var _ TeamGroupsHandler = (*NoopTeamGroupsREST)(nil)
|
||||
|
||||
type NoopTeamGroupsREST struct{}
|
||||
|
||||
func ProvideNoopTeamGroupsREST() *NoopTeamGroupsREST {
|
||||
return &NoopTeamGroupsREST{}
|
||||
}
|
||||
|
||||
// Connect implements rest.Connecter.
|
||||
func (n *NoopTeamGroupsREST) Connect(ctx context.Context, id string, options runtime.Object, r rest.Responder) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Error(w, "functionality not available", http.StatusForbidden)
|
||||
}), nil
|
||||
}
|
||||
|
||||
// New implements rest.Storage.
|
||||
func (s *NoopTeamGroupsREST) New() runtime.Object {
|
||||
return iamv0alpha1.NewGetGroups()
|
||||
}
|
||||
|
||||
// Destroy implements rest.Storage.
|
||||
func (s *NoopTeamGroupsREST) Destroy() {}
|
||||
|
||||
// NamespaceScoped implements rest.Scoper.
|
||||
func (s *NoopTeamGroupsREST) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// ProducesMIMETypes implements rest.StorageMetadata.
|
||||
func (s *NoopTeamGroupsREST) ProducesMIMETypes(verb string) []string {
|
||||
return []string{"application/json"}
|
||||
}
|
||||
|
||||
// ProducesObject implements rest.StorageMetadata.
|
||||
func (s *NoopTeamGroupsREST) ProducesObject(verb string) interface{} {
|
||||
return s.New()
|
||||
}
|
||||
|
||||
// NewConnectOptions implements rest.Connecter.
|
||||
func (s *NoopTeamGroupsREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
return nil, false, ""
|
||||
}
|
||||
|
||||
// ConnectMethods implements rest.Connecter.
|
||||
func (s *NoopTeamGroupsREST) ConnectMethods() []string {
|
||||
return []string{http.MethodGet}
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"github.com/grafana/authlib/types"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/externalgroupmapping"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/serviceaccount"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/sso"
|
||||
@@ -80,6 +81,8 @@ type IdentityAccessManagementAPIBuilder struct {
|
||||
unified resource.ResourceClient
|
||||
userSearchClient resourcepb.ResourceIndexClient
|
||||
|
||||
teamGroupsHandler externalgroupmapping.TeamGroupsHandler
|
||||
|
||||
// non-k8s api route
|
||||
display *user.LegacyDisplayREST
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ func RegisterAPIService(
|
||||
rolesStorage RoleStorageBackend,
|
||||
roleBindingsStorage RoleBindingStorageBackend,
|
||||
externalGroupMappingStorageBackend ExternalGroupMappingStorageBackend,
|
||||
teamGroupsHandlerImpl externalgroupmapping.TeamGroupsHandler,
|
||||
dual dualwrite.Service,
|
||||
unified resource.ResourceClient,
|
||||
userService legacyuser.Service,
|
||||
@@ -90,6 +91,7 @@ func RegisterAPIService(
|
||||
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
|
||||
roleBindingsStorage: roleBindingsStorage,
|
||||
externalGroupMappingStorage: externalGroupMappingStorageBackend,
|
||||
teamGroupsHandler: teamGroupsHandlerImpl,
|
||||
sso: ssoService,
|
||||
authorizer: authorizer,
|
||||
legacyAccessClient: legacyAccessClient,
|
||||
@@ -102,7 +104,8 @@ func RegisterAPIService(
|
||||
features: features,
|
||||
dual: dual,
|
||||
unified: unified,
|
||||
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(), unified, user.NewUserLegacySearchClient(userService), features),
|
||||
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(),
|
||||
unified, user.NewUserLegacySearchClient(userService), features),
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
|
||||
@@ -216,6 +219,7 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
|
||||
}
|
||||
|
||||
storage[teamResource.StoragePath("members")] = team.NewLegacyTeamMemberREST(b.store)
|
||||
storage[teamResource.StoragePath("groups")] = b.teamGroupsHandler
|
||||
|
||||
teamBindingResource := iamv0.TeamBindingResourceInfo
|
||||
teamBindingUniStore, err := grafanaregistry.NewRegistryStore(opts.Scheme, teamBindingResource, opts.OptsGetter)
|
||||
|
||||
@@ -20,6 +20,8 @@ const (
|
||||
UserResourceGroup = "iam.grafana.com"
|
||||
)
|
||||
|
||||
var _ resourcepb.ResourceIndexClient = (*UserLegacySearchClient)(nil)
|
||||
|
||||
// UserLegacySearchClient is a client for searching for users in the legacy search engine.
|
||||
type UserLegacySearchClient struct {
|
||||
resourcepb.ResourceIndexClient
|
||||
@@ -129,9 +131,9 @@ func getColumns(fields []string) []*resourcepb.ResourceTableColumnDefinition {
|
||||
for _, field := range fields {
|
||||
switch field {
|
||||
case "email":
|
||||
columns = append(columns, builders.TableColumnDefinitions[builders.USER_EMAIL])
|
||||
columns = append(columns, builders.UserTableColumnDefinitions[builders.USER_EMAIL])
|
||||
case "login":
|
||||
columns = append(columns, builders.TableColumnDefinitions[builders.USER_LOGIN])
|
||||
columns = append(columns, builders.UserTableColumnDefinitions[builders.USER_LOGIN])
|
||||
}
|
||||
}
|
||||
return columns
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/externalgroupmapping"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/ofrep"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/preferences"
|
||||
@@ -30,6 +31,9 @@ var WireSetExts = wire.NewSet(
|
||||
wire.Bind(new(iam.RoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.RoleBindingStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.ExternalGroupMappingStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
|
||||
externalgroupmapping.ProvideNoopTeamGroupsREST,
|
||||
wire.Bind(new(externalgroupmapping.TeamGroupsHandler), new(*externalgroupmapping.NoopTeamGroupsREST)),
|
||||
)
|
||||
|
||||
var provisioningExtras = wire.NewSet(
|
||||
|
||||
Generated
+5
-2
@@ -55,6 +55,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/registry/apis/datasource"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/folders"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/externalgroupmapping"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/ofrep"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/preferences"
|
||||
@@ -868,7 +869,8 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
}
|
||||
folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, accessClient, registerer, resourceClient, zanzanaClient)
|
||||
storageBackendImpl := noopstorage.ProvideStorageBackend()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, storageBackendImpl, dualwriteService, resourceClient, userService)
|
||||
noopTeamGroupsREST := externalgroupmapping.ProvideNoopTeamGroupsREST()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, userService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1517,7 +1519,8 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
}
|
||||
folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, accessClient, registerer, resourceClient, zanzanaClient)
|
||||
storageBackendImpl := noopstorage.ProvideStorageBackend()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, storageBackendImpl, dualwriteService, resourceClient, userService)
|
||||
noopTeamGroupsREST := externalgroupmapping.ProvideNoopTeamGroupsREST()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, userService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -62,5 +62,10 @@ func All(sql db.DB, sprinkles DashboardStats) ([]resource.DocumentBuilderInfo, e
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []resource.DocumentBuilderInfo{dashboards, users}, nil
|
||||
extGroupMappings, err := GetExternalGroupMappingBuilder()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []resource.DocumentBuilderInfo{dashboards, users, extGroupMappings}, nil
|
||||
}
|
||||
|
||||
@@ -58,6 +58,18 @@ func TestUserDocumentBuilder(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestExternalGroupMappingDocumentBuilder(t *testing.T) {
|
||||
info, err := GetExternalGroupMappingBuilder()
|
||||
require.NoError(t, err)
|
||||
doSnapshotTests(t, info.Builder, "external_group_mapping", &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
Group: "iam.grafana.app",
|
||||
Resource: "externalgroupmappings",
|
||||
}, []string{
|
||||
"mapping-with-team-and-group",
|
||||
})
|
||||
}
|
||||
|
||||
func TestDashboardDocumentBuilder(t *testing.T) {
|
||||
key := &resourcepb.ResourceKey{
|
||||
Namespace: "default",
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package builders
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resource"
|
||||
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
|
||||
)
|
||||
|
||||
const (
|
||||
EXTERNAL_GROUP_MAPPING_TEAM = "team"
|
||||
EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP = "external_group"
|
||||
)
|
||||
|
||||
var ExternalGroupMappingTableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
|
||||
EXTERNAL_GROUP_MAPPING_TEAM: {
|
||||
Name: EXTERNAL_GROUP_MAPPING_TEAM,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "The team name associated with the external group mapping",
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP: {
|
||||
Name: EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
Description: "The external group name/id associated with the external group mapping",
|
||||
Properties: &resourcepb.ResourceTableColumnDefinition_Properties{
|
||||
Filterable: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func GetExternalGroupMappingBuilder() (resource.DocumentBuilderInfo, error) {
|
||||
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(ExternalGroupMappingTableColumnDefinitions))
|
||||
for _, v := range ExternalGroupMappingTableColumnDefinitions {
|
||||
values = append(values, v)
|
||||
}
|
||||
|
||||
fields, err := resource.NewSearchableDocumentFields(values)
|
||||
return resource.DocumentBuilderInfo{
|
||||
GroupResource: iamv0.ExternalGroupMappingResourceInfo.GroupResource(),
|
||||
Fields: fields,
|
||||
Builder: new(extGroupMappingDocumentBuilder),
|
||||
}, err
|
||||
}
|
||||
|
||||
var _ resource.DocumentBuilder = new(extGroupMappingDocumentBuilder)
|
||||
|
||||
type extGroupMappingDocumentBuilder struct{}
|
||||
|
||||
// BuildDocument implements resource.DocumentBuilder.
|
||||
func (u *extGroupMappingDocumentBuilder) BuildDocument(ctx context.Context, key *resourcepb.ResourceKey, rv int64, value []byte) (*resource.IndexableDocument, error) {
|
||||
extGroupMapping := &iamv0.ExternalGroupMapping{}
|
||||
err := json.NewDecoder(bytes.NewReader(value)).Decode(extGroupMapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
obj, err := utils.MetaAccessor(extGroupMapping)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
doc := resource.NewIndexableDocument(key, rv, obj)
|
||||
|
||||
doc.Fields = make(map[string]any)
|
||||
if extGroupMapping.Spec.TeamRef.Name != "" {
|
||||
doc.Fields[EXTERNAL_GROUP_MAPPING_TEAM] = extGroupMapping.Spec.TeamRef.Name
|
||||
}
|
||||
if extGroupMapping.Spec.ExternalGroupId != "" {
|
||||
doc.Fields[EXTERNAL_GROUP_MAPPING_EXTERNAL_GROUP] = extGroupMapping.Spec.ExternalGroupId
|
||||
}
|
||||
|
||||
return doc, nil
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"key": {
|
||||
"namespace": "default",
|
||||
"group": "iam.grafana.app",
|
||||
"resource": "externalgroupmappings",
|
||||
"name": "mapping-with-team-and-group"
|
||||
},
|
||||
"name": "mapping-with-team-and-group",
|
||||
"rv": 1234,
|
||||
"fields": {
|
||||
"team": "team1",
|
||||
"external_group": "group1"
|
||||
},
|
||||
"title": "mapping-with-team-and-group",
|
||||
"title_ngram": "mapping-with-team-and-group",
|
||||
"title_phrase": "mapping-with-team-and-group"
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"apiVersion": "iam.grafana.app/v0alpha1",
|
||||
"kind": "ExternalGroupMapping",
|
||||
"metadata": {
|
||||
"name": "mapping-with-team-and-group",
|
||||
"namespace": "default"
|
||||
},
|
||||
"spec": {
|
||||
"teamRef": {
|
||||
"name": "team1"
|
||||
},
|
||||
"externalGroupId": "group1"
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@ const (
|
||||
USER_LOGIN = "login"
|
||||
)
|
||||
|
||||
var TableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
|
||||
var UserTableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinition{
|
||||
USER_EMAIL: {
|
||||
Name: USER_EMAIL,
|
||||
Type: resourcepb.ResourceTableColumnDefinition_STRING,
|
||||
@@ -38,8 +38,8 @@ var TableColumnDefinitions = map[string]*resourcepb.ResourceTableColumnDefinitio
|
||||
}
|
||||
|
||||
func GetUserBuilder() (resource.DocumentBuilderInfo, error) {
|
||||
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(TableColumnDefinitions))
|
||||
for _, v := range TableColumnDefinitions {
|
||||
values := make([]*resourcepb.ResourceTableColumnDefinition, 0, len(UserTableColumnDefinitions))
|
||||
for _, v := range UserTableColumnDefinitions {
|
||||
values = append(values, v)
|
||||
}
|
||||
fields, err := resource.NewSearchableDocumentFields(values)
|
||||
|
||||
@@ -4001,6 +4001,55 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/iam.grafana.app/v0alpha1/namespaces/{namespace}/teams/{name}/groups": {
|
||||
"get": {
|
||||
"tags": [
|
||||
"Team"
|
||||
],
|
||||
"description": "connect GET requests to groups of Team",
|
||||
"operationId": "getTeamGroups",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"content": {
|
||||
"*/*": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.GetGroups"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"x-kubernetes-action": "connect",
|
||||
"x-kubernetes-group-version-kind": {
|
||||
"group": "iam.grafana.app",
|
||||
"version": "v0alpha1",
|
||||
"kind": "GetGroups"
|
||||
}
|
||||
},
|
||||
"parameters": [
|
||||
{
|
||||
"name": "name",
|
||||
"in": "path",
|
||||
"description": "name of the GetGroups",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "namespace",
|
||||
"in": "path",
|
||||
"description": "object name and auth scope, such as for teams and projects",
|
||||
"required": true,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"uniqueItems": true
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"/apis/iam.grafana.app/v0alpha1/namespaces/{namespace}/teams/{name}/members": {
|
||||
"get": {
|
||||
"tags": [
|
||||
@@ -5050,6 +5099,40 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.GetGroups": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {},
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"x-kubernetes-group-version-kind": [
|
||||
{
|
||||
"group": "iam.grafana.app",
|
||||
"kind": "GetGroups",
|
||||
"version": "v0alpha1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.ServiceAccount": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -5549,6 +5632,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.apps.iam.pkg.apis.iam.v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"externalGroup"
|
||||
],
|
||||
"properties": {
|
||||
"externalGroup": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"com.github.grafana.grafana.pkg.apimachinery.apis.common.v0alpha1.Unstructured": {
|
||||
"type": "object",
|
||||
"additionalProperties": true,
|
||||
@@ -6237,6 +6337,42 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GetGroups": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
"type": "string"
|
||||
},
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {}
|
||||
}
|
||||
},
|
||||
"kind": {
|
||||
"description": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GetGroupsBody": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"items"
|
||||
],
|
||||
"properties": {
|
||||
"items": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"default": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
@@ -7573,6 +7709,23 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping": {
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"externalGroup"
|
||||
],
|
||||
"properties": {
|
||||
"externalGroup": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"default": ""
|
||||
}
|
||||
}
|
||||
},
|
||||
"io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": {
|
||||
"description": "APIResource specifies the name of a resource and whether it is namespaced.",
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user