From 93ec32dd6adfbbe621fcb4598d0ac0303df767d2 Mon Sep 17 00:00:00 2001 From: Misi Date: Tue, 25 Nov 2025 14:19:57 +0100 Subject: [PATCH] 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 --- apps/iam/go.mod | 8 +- apps/iam/kinds/team.cue | 14 ++ apps/iam/pkg/apis/iam/v0alpha1/register.go | 1 + .../pkg/apis/iam/v0alpha1/team_client_gen.go | 24 ++ .../team_getgroups_response_body_types_gen.go | 26 ++ ...eam_getgroups_response_object_types_gen.go | 37 +++ .../pkg/apis/iam/v0alpha1/zz_openapi_gen.go | 230 +++++++++++++----- apps/iam/pkg/apis/iam_manifest.go | 56 ++++- .../rtkq/iam/v0alpha1/endpoints.gen.ts | 22 ++ .../src/clients/rtkq/legacy/endpoints.gen.ts | 1 + .../apis/iam/externalgroupmapping/models.go | 10 + .../externalgroupmapping/team_groups_noop.go | 58 +++++ pkg/registry/apis/iam/models.go | 3 + pkg/registry/apis/iam/register.go | 6 +- pkg/registry/apis/iam/user/legacy_search.go | 6 +- pkg/registry/apis/wireset.go | 4 + pkg/server/wire_gen.go | 7 +- .../unified/search/builders/document.go | 7 +- .../unified/search/builders/document_test.go | 12 + .../search/builders/external_group_mapping.go | 80 ++++++ ...pping-mapping-with-team-and-group-out.json | 17 ++ ...p_mapping-mapping-with-team-and-group.json | 14 ++ pkg/storage/unified/search/builders/user.go | 6 +- .../iam.grafana.app-v0alpha1.json | 153 ++++++++++++ public/api-enterprise-spec.json | 3 + public/api-merged.json | 3 + public/openapi3.json | 3 + 27 files changed, 732 insertions(+), 79 deletions(-) create mode 100644 apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_body_types_gen.go create mode 100644 apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_object_types_gen.go create mode 100644 pkg/registry/apis/iam/externalgroupmapping/models.go create mode 100644 pkg/registry/apis/iam/externalgroupmapping/team_groups_noop.go create mode 100644 pkg/storage/unified/search/builders/external_group_mapping.go create mode 100644 pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group-out.json create mode 100644 pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group.json diff --git a/apps/iam/go.mod b/apps/iam/go.mod index e764e2a6158..73891acd454 100644 --- a/apps/iam/go.mod +++ b/apps/iam/go.mod @@ -14,16 +14,12 @@ replace github.com/grafana/grafana/apps/provisioning => ../provisioning replace github.com/grafana/grafana/apps/advisor => ../advisor -replace github.com/grafana/grafana/apps/annotation => ../annotation - replace github.com/grafana/grafana/apps/alerting/alertenrichment => ../alerting/alertenrichment replace github.com/grafana/grafana/apps/alerting/notifications => ../alerting/notifications replace github.com/grafana/grafana/apps/alerting/rules => ../alerting/rules -replace github.com/grafana/grafana/apps/collections => ../collections - replace github.com/grafana/grafana/apps/correlations => ../correlations replace github.com/grafana/grafana/apps/investigations => ../investigations @@ -46,6 +42,10 @@ replace github.com/grafana/grafana/pkg/apiserver => ../../pkg/apiserver replace github.com/grafana/grafana/pkg/aggregator => ../../pkg/aggregator +replace github.com/grafana/grafana/apps/annotation => ../annotation + +replace github.com/grafana/grafana/apps/collections => ../collections + replace github.com/prometheus/alertmanager => github.com/grafana/prometheus-alertmanager v0.25.1-0.20250911094103-5456b6e45604 require ( diff --git a/apps/iam/kinds/team.cue b/apps/iam/kinds/team.cue index 9acf9d60c01..412d8215999 100644 --- a/apps/iam/kinds/team.cue +++ b/apps/iam/kinds/team.cue @@ -18,4 +18,18 @@ teamv0alpha1: teamKind & { schema: { spec: v0alpha1.TeamSpec } + routes: { + "/groups": { + "GET": { + response: { + #ExternalGroupMapping: { + name: string + externalGroup: string + } + items: [...#ExternalGroupMapping] + } + responseMetadata: objectMeta: false + } + } + } } diff --git a/apps/iam/pkg/apis/iam/v0alpha1/register.go b/apps/iam/pkg/apis/iam/v0alpha1/register.go index 54d60c7bd6f..be70a0e1714 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/register.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/register.go @@ -321,6 +321,7 @@ func AddAuthNKnownTypes(scheme *runtime.Scheme) error { &TeamBindingList{}, &ExternalGroupMapping{}, &ExternalGroupMappingList{}, + &GetGroups{}, // For now these are registered in pkg/apis/iam/v0alpha1/register.go // &UserTeamList{}, // &ServiceAccountTokenList{}, diff --git a/apps/iam/pkg/apis/iam/v0alpha1/team_client_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/team_client_gen.go index 807c68dc0f6..2a6ce6caedb 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/team_client_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/team_client_gen.go @@ -2,6 +2,9 @@ package v0alpha1 import ( "context" + "encoding/json" + "fmt" + "net/http" "github.com/grafana/grafana-app-sdk/resource" ) @@ -78,3 +81,24 @@ func (c *TeamClient) Patch(ctx context.Context, identifier resource.Identifier, func (c *TeamClient) Delete(ctx context.Context, identifier resource.Identifier, opts resource.DeleteOptions) error { return c.client.Delete(ctx, identifier, opts) } + +type GetGroupsRequest struct { + Headers http.Header +} + +func (c *TeamClient) GetGroups(ctx context.Context, identifier resource.Identifier, request GetGroupsRequest) (*GetGroups, error) { + resp, err := c.client.SubresourceRequest(ctx, identifier, resource.CustomRouteRequestOptions{ + Path: "/groups", + Verb: "GET", + Headers: request.Headers, + }) + if err != nil { + return nil, err + } + cast := GetGroups{} + err = json.Unmarshal(resp, &cast) + if err != nil { + return nil, fmt.Errorf("unable to unmarshal response bytes into GetGroups: %w", err) + } + return &cast, nil +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_body_types_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_body_types_gen.go new file mode 100644 index 00000000000..553d7260c94 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_body_types_gen.go @@ -0,0 +1,26 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +// +k8s:openapi-gen=true +type VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping struct { + Name string `json:"name"` + ExternalGroup string `json:"externalGroup"` +} + +// NewVersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping creates a new VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping object. +func NewVersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping() *VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping { + return &VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping{} +} + +// +k8s:openapi-gen=true +type GetGroupsBody struct { + Items []VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping `json:"items"` +} + +// NewGetGroupsBody creates a new GetGroupsBody object. +func NewGetGroupsBody() *GetGroupsBody { + return &GetGroupsBody{ + Items: []VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping{}, + } +} diff --git a/apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_object_types_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_object_types_gen.go new file mode 100644 index 00000000000..3c8aa40a239 --- /dev/null +++ b/apps/iam/pkg/apis/iam/v0alpha1/team_getgroups_response_object_types_gen.go @@ -0,0 +1,37 @@ +// Code generated - EDITING IS FUTILE. DO NOT EDIT. + +package v0alpha1 + +import ( + "github.com/grafana/grafana-app-sdk/resource" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// +k8s:openapi-gen=true +type GetGroups struct { + metav1.TypeMeta `json:",inline"` + GetGroupsBody `json:",inline"` +} + +func NewGetGroups() *GetGroups { + return &GetGroups{} +} + +func (t *GetGroupsBody) DeepCopyInto(dst *GetGroupsBody) { + _ = resource.CopyObjectInto(dst, t) +} + +func (o *GetGroups) DeepCopyObject() runtime.Object { + dst := NewGetGroups() + o.DeepCopyInto(dst) + return dst +} + +func (o *GetGroups) DeepCopyInto(dst *GetGroups) { + dst.TypeMeta.APIVersion = o.TypeMeta.APIVersion + dst.TypeMeta.Kind = o.TypeMeta.Kind + o.GetGroupsBody.DeepCopyInto(&dst.GetGroupsBody) +} + +var _ runtime.Object = NewGetGroups() diff --git a/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go b/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go index f0c517afd38..e3b024799d7 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/zz_openapi_gen.go @@ -12,71 +12,74 @@ import ( func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition { return map[string]common.OpenAPIDefinition{ - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRole": schema_pkg_apis_iam_v0alpha1_CoreRole(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleList": schema_pkg_apis_iam_v0alpha1_CoreRoleList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleSpec": schema_pkg_apis_iam_v0alpha1_CoreRoleSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleStatus": schema_pkg_apis_iam_v0alpha1_CoreRoleStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolespecPermission": schema_pkg_apis_iam_v0alpha1_CoreRolespecPermission(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_CoreRolestatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMapping": schema_pkg_apis_iam_v0alpha1_ExternalGroupMapping(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMappingList": schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMappingSpec": schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMappingTeamRef": schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingTeamRef(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole": schema_pkg_apis_iam_v0alpha1_GlobalRole(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBinding": schema_pkg_apis_iam_v0alpha1_GlobalRoleBinding(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingList": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingSpec": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingStatus": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecRoleRef": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecRoleRef(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecSubject": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecSubject(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingstatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleList": schema_pkg_apis_iam_v0alpha1_GlobalRoleList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleSpec": schema_pkg_apis_iam_v0alpha1_GlobalRoleSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleStatus": schema_pkg_apis_iam_v0alpha1_GlobalRoleStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolespecPermission": schema_pkg_apis_iam_v0alpha1_GlobalRolespecPermission(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_GlobalRolestatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermission": schema_pkg_apis_iam_v0alpha1_ResourcePermission(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionList": schema_pkg_apis_iam_v0alpha1_ResourcePermissionList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionSpec": schema_pkg_apis_iam_v0alpha1_ResourcePermissionSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionStatus": schema_pkg_apis_iam_v0alpha1_ResourcePermissionStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecPermission": schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecPermission(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecResource": schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecResource(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionstatusOperatorState": schema_pkg_apis_iam_v0alpha1_ResourcePermissionstatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Role": schema_pkg_apis_iam_v0alpha1_Role(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBinding": schema_pkg_apis_iam_v0alpha1_RoleBinding(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingList": schema_pkg_apis_iam_v0alpha1_RoleBindingList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingSpec": schema_pkg_apis_iam_v0alpha1_RoleBindingSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingStatus": schema_pkg_apis_iam_v0alpha1_RoleBindingStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecRoleRef": schema_pkg_apis_iam_v0alpha1_RoleBindingspecRoleRef(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecSubject": schema_pkg_apis_iam_v0alpha1_RoleBindingspecSubject(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_RoleBindingstatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleList": schema_pkg_apis_iam_v0alpha1_RoleList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleSpec": schema_pkg_apis_iam_v0alpha1_RoleSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleStatus": schema_pkg_apis_iam_v0alpha1_RoleStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolespecPermission": schema_pkg_apis_iam_v0alpha1_RolespecPermission(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_RolestatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccount": schema_pkg_apis_iam_v0alpha1_ServiceAccount(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountList": schema_pkg_apis_iam_v0alpha1_ServiceAccountList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountSpec": schema_pkg_apis_iam_v0alpha1_ServiceAccountSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountStatus": schema_pkg_apis_iam_v0alpha1_ServiceAccountStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountstatusOperatorState": schema_pkg_apis_iam_v0alpha1_ServiceAccountstatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Team": schema_pkg_apis_iam_v0alpha1_Team(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBinding": schema_pkg_apis_iam_v0alpha1_TeamBinding(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingList": schema_pkg_apis_iam_v0alpha1_TeamBindingList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingSpec": schema_pkg_apis_iam_v0alpha1_TeamBindingSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingStatus": schema_pkg_apis_iam_v0alpha1_TeamBindingStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingTeamRef": schema_pkg_apis_iam_v0alpha1_TeamBindingTeamRef(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingspecSubject": schema_pkg_apis_iam_v0alpha1_TeamBindingspecSubject(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_TeamBindingstatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamList": schema_pkg_apis_iam_v0alpha1_TeamList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamSpec": schema_pkg_apis_iam_v0alpha1_TeamSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamStatus": schema_pkg_apis_iam_v0alpha1_TeamStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamstatusOperatorState": schema_pkg_apis_iam_v0alpha1_TeamstatusOperatorState(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.User": schema_pkg_apis_iam_v0alpha1_User(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserList": schema_pkg_apis_iam_v0alpha1_UserList(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserSpec": schema_pkg_apis_iam_v0alpha1_UserSpec(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserStatus": schema_pkg_apis_iam_v0alpha1_UserStatus(ref), - "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserstatusOperatorState": schema_pkg_apis_iam_v0alpha1_UserstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRole": schema_pkg_apis_iam_v0alpha1_CoreRole(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleList": schema_pkg_apis_iam_v0alpha1_CoreRoleList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleSpec": schema_pkg_apis_iam_v0alpha1_CoreRoleSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRoleStatus": schema_pkg_apis_iam_v0alpha1_CoreRoleStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolespecPermission": schema_pkg_apis_iam_v0alpha1_CoreRolespecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.CoreRolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_CoreRolestatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMapping": schema_pkg_apis_iam_v0alpha1_ExternalGroupMapping(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMappingList": schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMappingSpec": schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ExternalGroupMappingTeamRef": schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingTeamRef(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GetGroups": schema_pkg_apis_iam_v0alpha1_GetGroups(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GetGroupsBody": schema_pkg_apis_iam_v0alpha1_GetGroupsBody(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRole": schema_pkg_apis_iam_v0alpha1_GlobalRole(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBinding": schema_pkg_apis_iam_v0alpha1_GlobalRoleBinding(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingList": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingSpec": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingStatus": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecRoleRef": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecRoleRef(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingspecSubject": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingspecSubject(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_GlobalRoleBindingstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleList": schema_pkg_apis_iam_v0alpha1_GlobalRoleList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleSpec": schema_pkg_apis_iam_v0alpha1_GlobalRoleSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRoleStatus": schema_pkg_apis_iam_v0alpha1_GlobalRoleStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolespecPermission": schema_pkg_apis_iam_v0alpha1_GlobalRolespecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.GlobalRolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_GlobalRolestatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermission": schema_pkg_apis_iam_v0alpha1_ResourcePermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionList": schema_pkg_apis_iam_v0alpha1_ResourcePermissionList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionSpec": schema_pkg_apis_iam_v0alpha1_ResourcePermissionSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionStatus": schema_pkg_apis_iam_v0alpha1_ResourcePermissionStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecPermission": schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionspecResource": schema_pkg_apis_iam_v0alpha1_ResourcePermissionspecResource(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ResourcePermissionstatusOperatorState": schema_pkg_apis_iam_v0alpha1_ResourcePermissionstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Role": schema_pkg_apis_iam_v0alpha1_Role(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBinding": schema_pkg_apis_iam_v0alpha1_RoleBinding(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingList": schema_pkg_apis_iam_v0alpha1_RoleBindingList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingSpec": schema_pkg_apis_iam_v0alpha1_RoleBindingSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingStatus": schema_pkg_apis_iam_v0alpha1_RoleBindingStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecRoleRef": schema_pkg_apis_iam_v0alpha1_RoleBindingspecRoleRef(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingspecSubject": schema_pkg_apis_iam_v0alpha1_RoleBindingspecSubject(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_RoleBindingstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleList": schema_pkg_apis_iam_v0alpha1_RoleList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleSpec": schema_pkg_apis_iam_v0alpha1_RoleSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RoleStatus": schema_pkg_apis_iam_v0alpha1_RoleStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolespecPermission": schema_pkg_apis_iam_v0alpha1_RolespecPermission(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.RolestatusOperatorState": schema_pkg_apis_iam_v0alpha1_RolestatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccount": schema_pkg_apis_iam_v0alpha1_ServiceAccount(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountList": schema_pkg_apis_iam_v0alpha1_ServiceAccountList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountSpec": schema_pkg_apis_iam_v0alpha1_ServiceAccountSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountStatus": schema_pkg_apis_iam_v0alpha1_ServiceAccountStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.ServiceAccountstatusOperatorState": schema_pkg_apis_iam_v0alpha1_ServiceAccountstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.Team": schema_pkg_apis_iam_v0alpha1_Team(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBinding": schema_pkg_apis_iam_v0alpha1_TeamBinding(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingList": schema_pkg_apis_iam_v0alpha1_TeamBindingList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingSpec": schema_pkg_apis_iam_v0alpha1_TeamBindingSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingStatus": schema_pkg_apis_iam_v0alpha1_TeamBindingStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingTeamRef": schema_pkg_apis_iam_v0alpha1_TeamBindingTeamRef(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingspecSubject": schema_pkg_apis_iam_v0alpha1_TeamBindingspecSubject(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamBindingstatusOperatorState": schema_pkg_apis_iam_v0alpha1_TeamBindingstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamList": schema_pkg_apis_iam_v0alpha1_TeamList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamSpec": schema_pkg_apis_iam_v0alpha1_TeamSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamStatus": schema_pkg_apis_iam_v0alpha1_TeamStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.TeamstatusOperatorState": schema_pkg_apis_iam_v0alpha1_TeamstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.User": schema_pkg_apis_iam_v0alpha1_User(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserList": schema_pkg_apis_iam_v0alpha1_UserList(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserSpec": schema_pkg_apis_iam_v0alpha1_UserSpec(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserStatus": schema_pkg_apis_iam_v0alpha1_UserStatus(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.UserstatusOperatorState": schema_pkg_apis_iam_v0alpha1_UserstatusOperatorState(ref), + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping": schema_pkg_apis_iam_v0alpha1_VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping(ref), } } @@ -491,6 +494,76 @@ func schema_pkg_apis_iam_v0alpha1_ExternalGroupMappingTeamRef(ref common.Referen } } +func schema_pkg_apis_iam_v0alpha1_GetGroups(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "kind": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "apiVersion": { + SchemaProps: spec.SchemaProps{ + 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{"string"}, + Format: "", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping"}, + } +} + +func schema_pkg_apis_iam_v0alpha1_GetGroupsBody(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + Items: &spec.SchemaOrArray{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Default: map[string]interface{}{}, + Ref: ref("github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping"), + }, + }, + }, + }, + }, + }, + Required: []string{"items"}, + }, + }, + Dependencies: []string{ + "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1.VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping"}, + } +} + func schema_pkg_apis_iam_v0alpha1_GlobalRole(ref common.ReferenceCallback) common.OpenAPIDefinition { return common.OpenAPIDefinition{ Schema: spec.Schema{ @@ -2856,3 +2929,30 @@ func schema_pkg_apis_iam_v0alpha1_UserstatusOperatorState(ref common.ReferenceCa }, } } + +func schema_pkg_apis_iam_v0alpha1_VersionsV0alpha1Kinds7RoutesGroupsGETResponseExternalGroupMapping(ref common.ReferenceCallback) common.OpenAPIDefinition { + return common.OpenAPIDefinition{ + Schema: spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "name": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + "externalGroup": { + SchemaProps: spec.SchemaProps{ + Default: "", + Type: []string{"string"}, + Format: "", + }, + }, + }, + Required: []string{"name", "externalGroup"}, + }, + }, + } +} diff --git a/apps/iam/pkg/apis/iam_manifest.go b/apps/iam/pkg/apis/iam_manifest.go index 388c0dca609..9fe171525b4 100644 --- a/apps/iam/pkg/apis/iam_manifest.go +++ b/apps/iam/pkg/apis/iam_manifest.go @@ -81,6 +81,58 @@ var appManifestData = app.ManifestData{ Plural: "Teams", Scope: "Namespaced", Conversion: false, + Routes: map[string]spec3.PathProps{ + "/groups": { + Get: &spec3.Operation{ + OperationProps: spec3.OperationProps{ + + OperationId: "getGroups", + + Responses: &spec3.Responses{ + ResponsesProps: spec3.ResponsesProps{ + Default: &spec3.Response{ + ResponseProps: spec3.ResponseProps{ + Description: "Default OK response", + Content: map[string]*spec3.MediaType{ + "application/json": { + MediaTypeProps: spec3.MediaTypeProps{ + Schema: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "apiVersion": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + 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", + }, + }, + "items": { + SchemaProps: spec.SchemaProps{ + Type: []string{"array"}, + }, + }, + "kind": { + SchemaProps: spec.SchemaProps{ + Type: []string{"string"}, + 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", + }, + }, + }, + Required: []string{ + "items", + "apiVersion", + "kind", + }, + }}, + }}, + }, + }, + }, + }}, + }, + }, + }, + }, }, { @@ -142,7 +194,9 @@ func ManifestGoTypeAssociator(kind, version string) (goType resource.Kind, exist return goType, exists } -var customRouteToGoResponseType = map[string]any{} +var customRouteToGoResponseType = map[string]any{ + "v0alpha1|Team|groups|GET": v0alpha1.GetGroups{}, +} // ManifestCustomRouteResponsesAssociator returns the associated response go type for a given kind, version, custom route path, and method, if one exists. // kind may be empty for custom routes which are not kind subroutes. Leading slashes are removed from subroute paths. diff --git a/packages/grafana-api-clients/src/clients/rtkq/iam/v0alpha1/endpoints.gen.ts b/packages/grafana-api-clients/src/clients/rtkq/iam/v0alpha1/endpoints.gen.ts index 851680b4be4..88ce5985631 100644 --- a/packages/grafana-api-clients/src/clients/rtkq/iam/v0alpha1/endpoints.gen.ts +++ b/packages/grafana-api-clients/src/clients/rtkq/iam/v0alpha1/endpoints.gen.ts @@ -561,6 +561,10 @@ const injectedRtkApi = api }), invalidatesTags: ['Team'], }), + getTeamGroups: build.query({ + query: (queryArg) => ({ url: `/teams/${queryArg.name}/groups` }), + providesTags: ['Team'], + }), getTeamMembers: build.query({ query: (queryArg) => ({ url: `/teams/${queryArg.name}/members` }), providesTags: ['Team'], @@ -1461,6 +1465,11 @@ export type UpdateTeamApiArg = { force?: boolean; patch: Patch; }; +export type GetTeamGroupsApiResponse = /** status 200 OK */ GetGroups; +export type GetTeamGroupsApiArg = { + /** name of the GetGroups */ + name: string; +}; export type GetTeamMembersApiResponse = /** status 200 OK */ TeamMemberList; export type GetTeamMembersApiArg = { /** name of the TeamMemberList */ @@ -1978,6 +1987,17 @@ export type TeamList = { kind?: string; metadata: ListMeta; }; +export type VersionsV0Alpha1Kinds7RoutesGroupsGetResponseExternalGroupMapping = { + externalGroup: string; + name: string; +}; +export type GetGroups = { + /** 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 */ + apiVersion?: string; + items: VersionsV0Alpha1Kinds7RoutesGroupsGetResponseExternalGroupMapping[]; + /** 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 */ + kind?: string; +}; export type TeamMember = { /** AvatarURL is the url where we can get the avatar for identity */ avatarURL?: string; @@ -2100,6 +2120,8 @@ export const { useReplaceTeamMutation, useDeleteTeamMutation, useUpdateTeamMutation, + useGetTeamGroupsQuery, + useLazyGetTeamGroupsQuery, useGetTeamMembersQuery, useLazyGetTeamMembersQuery, useListUserQuery, diff --git a/packages/grafana-api-clients/src/clients/rtkq/legacy/endpoints.gen.ts b/packages/grafana-api-clients/src/clients/rtkq/legacy/endpoints.gen.ts index b9be8da0043..45ff0d1e91e 100644 --- a/packages/grafana-api-clients/src/clients/rtkq/legacy/endpoints.gen.ts +++ b/packages/grafana-api-clients/src/clients/rtkq/legacy/endpoints.gen.ts @@ -6039,6 +6039,7 @@ export type TeamGroupDto = { groupId?: string; orgId?: number; teamId?: number; + uid?: string; }; export type TeamGroupMapping = { groupId?: string; diff --git a/pkg/registry/apis/iam/externalgroupmapping/models.go b/pkg/registry/apis/iam/externalgroupmapping/models.go new file mode 100644 index 00000000000..4a8f2e5df08 --- /dev/null +++ b/pkg/registry/apis/iam/externalgroupmapping/models.go @@ -0,0 +1,10 @@ +package externalgroupmapping + +import "k8s.io/apiserver/pkg/registry/rest" + +type TeamGroupsHandler interface { + rest.Storage + rest.Scoper + rest.StorageMetadata + rest.Connecter +} diff --git a/pkg/registry/apis/iam/externalgroupmapping/team_groups_noop.go b/pkg/registry/apis/iam/externalgroupmapping/team_groups_noop.go new file mode 100644 index 00000000000..9333c50f666 --- /dev/null +++ b/pkg/registry/apis/iam/externalgroupmapping/team_groups_noop.go @@ -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} +} diff --git a/pkg/registry/apis/iam/models.go b/pkg/registry/apis/iam/models.go index 309b266ccb4..35efb2f2155 100644 --- a/pkg/registry/apis/iam/models.go +++ b/pkg/registry/apis/iam/models.go @@ -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 diff --git a/pkg/registry/apis/iam/register.go b/pkg/registry/apis/iam/register.go index 2f214fd0630..a464568c12f 100644 --- a/pkg/registry/apis/iam/register.go +++ b/pkg/registry/apis/iam/register.go @@ -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) diff --git a/pkg/registry/apis/iam/user/legacy_search.go b/pkg/registry/apis/iam/user/legacy_search.go index 7459e7074e4..534a4f016eb 100644 --- a/pkg/registry/apis/iam/user/legacy_search.go +++ b/pkg/registry/apis/iam/user/legacy_search.go @@ -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 diff --git a/pkg/registry/apis/wireset.go b/pkg/registry/apis/wireset.go index 5d76f64dfcd..d19b405c6c9 100644 --- a/pkg/registry/apis/wireset.go +++ b/pkg/registry/apis/wireset.go @@ -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( diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index 9d4dcab2f7e..279a67f4134 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -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 } diff --git a/pkg/storage/unified/search/builders/document.go b/pkg/storage/unified/search/builders/document.go index 24226cdcc9a..675f95b4b78 100644 --- a/pkg/storage/unified/search/builders/document.go +++ b/pkg/storage/unified/search/builders/document.go @@ -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 } diff --git a/pkg/storage/unified/search/builders/document_test.go b/pkg/storage/unified/search/builders/document_test.go index 59c3302c105..bee719aeefa 100644 --- a/pkg/storage/unified/search/builders/document_test.go +++ b/pkg/storage/unified/search/builders/document_test.go @@ -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", diff --git a/pkg/storage/unified/search/builders/external_group_mapping.go b/pkg/storage/unified/search/builders/external_group_mapping.go new file mode 100644 index 00000000000..809075c2ec2 --- /dev/null +++ b/pkg/storage/unified/search/builders/external_group_mapping.go @@ -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 +} diff --git a/pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group-out.json b/pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group-out.json new file mode 100644 index 00000000000..935ff7b34b7 --- /dev/null +++ b/pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group-out.json @@ -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" +} \ No newline at end of file diff --git a/pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group.json b/pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group.json new file mode 100644 index 00000000000..c34f8ae04ab --- /dev/null +++ b/pkg/storage/unified/search/builders/testdata/doc/external_group_mapping-mapping-with-team-and-group.json @@ -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" + } +} diff --git a/pkg/storage/unified/search/builders/user.go b/pkg/storage/unified/search/builders/user.go index fdf1fce25e1..b17cdb99b15 100644 --- a/pkg/storage/unified/search/builders/user.go +++ b/pkg/storage/unified/search/builders/user.go @@ -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) diff --git a/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json b/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json index d0ac7bbfff3..8449498a061 100644 --- a/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json +++ b/pkg/tests/apis/openapi_snapshots/iam.grafana.app-v0alpha1.json @@ -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", diff --git a/public/api-enterprise-spec.json b/public/api-enterprise-spec.json index a7dfb2004ec..c976dcb9e7d 100644 --- a/public/api-enterprise-spec.json +++ b/public/api-enterprise-spec.json @@ -7967,6 +7967,9 @@ "teamId": { "type": "integer", "format": "int64" + }, + "uid": { + "type": "string" } } }, diff --git a/public/api-merged.json b/public/api-merged.json index ace7476c7d5..0d9c4129adc 100644 --- a/public/api-merged.json +++ b/public/api-merged.json @@ -22070,6 +22070,9 @@ "teamId": { "type": "integer", "format": "int64" + }, + "uid": { + "type": "string" } } }, diff --git a/public/openapi3.json b/public/openapi3.json index eaa4082ab48..20b97b3f14d 100644 --- a/public/openapi3.json +++ b/public/openapi3.json @@ -11609,6 +11609,9 @@ "teamId": { "format": "int64", "type": "integer" + }, + "uid": { + "type": "string" } }, "type": "object"