| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | package iam | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2025-07-04 17:07:48 +08:00
										 |  |  | 	"maps" | 
					
						
							| 
									
										
										
										
											2025-02-03 21:40:36 +08:00
										 |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	"github.com/prometheus/client_golang/prometheus" | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/runtime" | 
					
						
							|  |  |  | 	"k8s.io/apimachinery/pkg/runtime/schema" | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 	"k8s.io/apiserver/pkg/admission" | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	"k8s.io/apiserver/pkg/authorization/authorizer" | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	"k8s.io/apiserver/pkg/registry/generic" | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	"k8s.io/apiserver/pkg/registry/rest" | 
					
						
							|  |  |  | 	genericapiserver "k8s.io/apiserver/pkg/server" | 
					
						
							|  |  |  | 	common "k8s.io/kube-openapi/pkg/common" | 
					
						
							| 
									
										
										
										
											2025-02-03 21:40:36 +08:00
										 |  |  | 	"k8s.io/kube-openapi/pkg/spec3" | 
					
						
							| 
									
										
										
										
											2025-02-03 19:24:35 +08:00
										 |  |  | 	"k8s.io/kube-openapi/pkg/validation/spec" | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-21 17:06:55 +08:00
										 |  |  | 	"github.com/grafana/authlib/types" | 
					
						
							| 
									
										
										
										
											2025-09-29 20:28:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1" | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/apimachinery/identity" | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/apimachinery/utils" | 
					
						
							|  |  |  | 	legacyiamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1" | 
					
						
							|  |  |  | 	grafanaregistry "github.com/grafana/grafana/pkg/apiserver/registry/generic" | 
					
						
							|  |  |  | 	grafanarest "github.com/grafana/grafana/pkg/apiserver/rest" | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/infra/db" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/registry/apis/iam/legacy" | 
					
						
							| 
									
										
										
										
											2025-08-27 21:00:09 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/registry/apis/iam/resourcepermission" | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/registry/apis/iam/serviceaccount" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/registry/apis/iam/sso" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/registry/apis/iam/team" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/registry/apis/iam/user" | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/accesscontrol" | 
					
						
							| 
									
										
										
										
											2025-09-11 23:46:29 +08:00
										 |  |  | 	gfauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer" | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/apiserver/builder" | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/featuremgmt" | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/ssosettings" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/storage/legacysql" | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/apistore" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/storage/unified/resource" | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func RegisterAPIService( | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	features featuremgmt.FeatureToggles, | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 	apiregistration builder.APIRegistrar, | 
					
						
							| 
									
										
										
										
											2024-08-21 15:16:47 +08:00
										 |  |  | 	ssoService ssosettings.Service, | 
					
						
							| 
									
										
										
										
											2024-08-15 19:38:43 +08:00
										 |  |  | 	sql db.DB, | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	ac accesscontrol.AccessControl, | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	accessClient types.AccessClient, | 
					
						
							|  |  |  | 	reg prometheus.Registerer, | 
					
						
							|  |  |  | 	coreRolesStorage CoreRoleStorageBackend, | 
					
						
							| 
									
										
										
										
											2025-07-28 19:36:27 +08:00
										 |  |  | 	rolesStorage RoleStorageBackend, | 
					
						
							| 
									
										
										
										
											2025-09-29 20:28:12 +08:00
										 |  |  | 	roleBindingsStorage RoleBindingStorageBackend, | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | ) (*IdentityAccessManagementAPIBuilder, error) { | 
					
						
							| 
									
										
										
										
											2025-08-27 21:00:09 +08:00
										 |  |  | 	dbProvider := legacysql.NewDatabaseProvider(sql) | 
					
						
							|  |  |  | 	store := legacy.NewLegacySQLStores(dbProvider) | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	legacyAccessClient := newLegacyAccessClient(ac, store) | 
					
						
							|  |  |  | 	authorizer := newIAMAuthorizer(accessClient, legacyAccessClient) | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | 	builder := &IdentityAccessManagementAPIBuilder{ | 
					
						
							| 
									
										
										
										
											2025-08-27 21:00:09 +08:00
										 |  |  | 		store:                        store, | 
					
						
							|  |  |  | 		coreRolesStorage:             coreRolesStorage, | 
					
						
							|  |  |  | 		rolesStorage:                 rolesStorage, | 
					
						
							|  |  |  | 		resourcePermissionsStorage:   resourcepermission.ProvideStorageBackend(dbProvider), | 
					
						
							| 
									
										
										
										
											2025-09-29 20:28:12 +08:00
										 |  |  | 		roleBindingsStorage:          roleBindingsStorage, | 
					
						
							| 
									
										
										
										
											2025-08-27 21:00:09 +08:00
										 |  |  | 		sso:                          ssoService, | 
					
						
							|  |  |  | 		authorizer:                   authorizer, | 
					
						
							|  |  |  | 		legacyAccessClient:           legacyAccessClient, | 
					
						
							|  |  |  | 		accessClient:                 accessClient, | 
					
						
							|  |  |  | 		display:                      user.NewLegacyDisplayREST(store), | 
					
						
							|  |  |  | 		reg:                          reg, | 
					
						
							|  |  |  | 		enableAuthZApis:              features.IsEnabledGlobally(featuremgmt.FlagKubernetesAuthzApis), | 
					
						
							|  |  |  | 		enableResourcePermissionApis: features.IsEnabledGlobally(featuremgmt.FlagKubernetesAuthzResourcePermissionApis), | 
					
						
							|  |  |  | 		enableAuthnMutation:          features.IsEnabledGlobally(featuremgmt.FlagKubernetesAuthnMutation), | 
					
						
							|  |  |  | 		enableDualWriter:             true, | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	apiregistration.RegisterAPI(builder) | 
					
						
							| 
									
										
										
										
											2024-08-21 15:16:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-08-15 19:38:43 +08:00
										 |  |  | 	return builder, nil | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-11 23:46:29 +08:00
										 |  |  | func NewAPIService( | 
					
						
							|  |  |  | 	accessClient types.AccessClient, | 
					
						
							|  |  |  | 	dbProvider legacysql.LegacyDatabaseProvider, | 
					
						
							|  |  |  | 	enabledApis map[string]bool, | 
					
						
							|  |  |  | ) *IdentityAccessManagementAPIBuilder { | 
					
						
							|  |  |  | 	store := legacy.NewLegacySQLStores(dbProvider) | 
					
						
							|  |  |  | 	resourcePermissionsStorage := resourcepermission.ProvideStorageBackend(dbProvider) | 
					
						
							|  |  |  | 	resourceAuthorizer := gfauthorizer.NewResourceAuthorizer(accessClient) | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	return &IdentityAccessManagementAPIBuilder{ | 
					
						
							| 
									
										
										
										
											2025-09-11 23:46:29 +08:00
										 |  |  | 		store:                        store, | 
					
						
							|  |  |  | 		display:                      user.NewLegacyDisplayREST(store), | 
					
						
							|  |  |  | 		resourcePermissionsStorage:   resourcePermissionsStorage, | 
					
						
							|  |  |  | 		enableResourcePermissionApis: enabledApis["resourcepermissions"], | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 		authorizer: authorizer.AuthorizerFunc( | 
					
						
							|  |  |  | 			func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) { | 
					
						
							| 
									
										
										
										
											2025-09-11 23:46:29 +08:00
										 |  |  | 				// For now only authorize resourcepermissions resource
 | 
					
						
							|  |  |  | 				if a.GetResource() == "resourcepermissions" { | 
					
						
							|  |  |  | 					return resourceAuthorizer.Authorize(ctx, a) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 				user, err := identity.GetRequester(ctx) | 
					
						
							|  |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return authorizer.DecisionDeny, "no identity found", err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				if user.GetIsGrafanaAdmin() { | 
					
						
							|  |  |  | 					return authorizer.DecisionAllow, "", nil | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				return authorizer.DecisionDeny, "only grafana admins have access for now", nil | 
					
						
							|  |  |  | 			}), | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) GetGroupVersion() schema.GroupVersion { | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	return legacyiamv0.SchemeGroupVersion | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) InstallSchema(scheme *runtime.Scheme) error { | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	if b.enableAuthZApis { | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 		if err := iamv0.AddAuthZKnownTypes(scheme); err != nil { | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-11 23:46:29 +08:00
										 |  |  | 	if b.enableResourcePermissionApis { | 
					
						
							|  |  |  | 		if err := iamv0.AddResourcePermissionKnownTypes(scheme, iamv0.SchemeGroupVersion); err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 	if err := iamv0.AddAuthNKnownTypes(scheme); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	legacyiamv0.AddKnownTypes(scheme, legacyiamv0.VERSION) | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Link this version to the internal representation.
 | 
					
						
							|  |  |  | 	// This is used for server-side-apply (PATCH), and avoids the error:
 | 
					
						
							| 
									
										
										
										
											2024-08-21 15:16:47 +08:00
										 |  |  | 	// "no kind is registered for the type"
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	legacyiamv0.AddKnownTypes(scheme, runtime.APIVersionInternal) | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 	metav1.AddToGroupVersion(scheme, iamv0.SchemeGroupVersion) | 
					
						
							|  |  |  | 	return scheme.SetVersionPriority(iamv0.SchemeGroupVersion) | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-21 05:37:17 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) AllowedV0Alpha1Resources() []string { | 
					
						
							|  |  |  | 	return []string{builder.AllResourcesAllowed} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error { | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 	storage := map[string]rest.Storage{} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-30 20:59:33 +08:00
										 |  |  | 	// teams + users must have shorter names because they are often used as part of another name
 | 
					
						
							|  |  |  | 	opts.StorageOptsRegister(iamv0.TeamResourceInfo.GroupResource(), apistore.StorageOptions{ | 
					
						
							|  |  |  | 		MaximumNameLength: 80, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	opts.StorageOptsRegister(iamv0.UserResourceInfo.GroupResource(), apistore.StorageOptions{ | 
					
						
							|  |  |  | 		MaximumNameLength: 80, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 	teamResource := iamv0.TeamResourceInfo | 
					
						
							| 
									
										
										
										
											2025-09-18 19:43:07 +08:00
										 |  |  | 	teamLegacyStore := team.NewLegacyStore(b.store, b.legacyAccessClient, b.enableAuthnMutation) | 
					
						
							|  |  |  | 	storage[teamResource.StoragePath()] = teamLegacyStore | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	storage[teamResource.StoragePath("members")] = team.NewLegacyTeamMemberREST(b.store) | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-18 19:43:07 +08:00
										 |  |  | 	if b.enableDualWriter { | 
					
						
							|  |  |  | 		teamStore, err := grafanaregistry.NewRegistryStore(opts.Scheme, teamResource, opts.OptsGetter) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		teamDW, err := opts.DualWriteBuilder(teamResource.GroupResource(), teamLegacyStore, teamStore) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		storage[teamResource.StoragePath()] = teamDW | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 	teamBindingResource := iamv0.TeamBindingResourceInfo | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	storage[teamBindingResource.StoragePath()] = team.NewLegacyBindingStore(b.store) | 
					
						
							| 
									
										
										
										
											2024-08-27 14:31:29 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 	// User store registration
 | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 	userResource := iamv0.UserResourceInfo | 
					
						
							| 
									
										
										
										
											2025-09-24 17:32:29 +08:00
										 |  |  | 	legacyStore := user.NewLegacyStore(b.store, b.accessClient, b.enableAuthnMutation) | 
					
						
							| 
									
										
										
										
											2025-07-24 20:33:53 +08:00
										 |  |  | 	storage[userResource.StoragePath()] = legacyStore | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 20:33:53 +08:00
										 |  |  | 	if b.enableDualWriter { | 
					
						
							|  |  |  | 		store, err := grafanaregistry.NewRegistryStore(opts.Scheme, userResource, opts.OptsGetter) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-07-18 17:29:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 20:33:53 +08:00
										 |  |  | 		dw, err := opts.DualWriteBuilder(userResource.GroupResource(), legacyStore, store) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 20:33:53 +08:00
										 |  |  | 		storage[userResource.StoragePath()] = dw | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-08-21 15:16:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-24 20:33:53 +08:00
										 |  |  | 	storage[userResource.StoragePath("teams")] = user.NewLegacyTeamMemberREST(b.store) | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Service Accounts store registration
 | 
					
						
							| 
									
										
										
										
											2025-08-28 18:32:15 +08:00
										 |  |  | 	serviceAccountResource := iamv0.ServiceAccountResourceInfo | 
					
						
							| 
									
										
										
										
											2025-09-24 17:32:29 +08:00
										 |  |  | 	saLegacyStore := serviceaccount.NewLegacyStore(b.store, b.accessClient, b.enableAuthnMutation) | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 	storage[serviceAccountResource.StoragePath()] = saLegacyStore | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if b.enableDualWriter { | 
					
						
							|  |  |  | 		store, err := grafanaregistry.NewRegistryStore(opts.Scheme, serviceAccountResource, opts.OptsGetter) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		dw, err := opts.DualWriteBuilder(serviceAccountResource.GroupResource(), saLegacyStore, store) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		storage[serviceAccountResource.StoragePath()] = dw | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-24 21:13:04 +08:00
										 |  |  | 	storage[serviceAccountResource.StoragePath("tokens")] = serviceaccount.NewLegacyTokenREST(b.store) | 
					
						
							| 
									
										
										
										
											2024-08-21 15:16:47 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	if b.sso != nil { | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 		ssoResource := legacyiamv0.SSOSettingResourceInfo | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 		storage[ssoResource.StoragePath()] = sso.NewLegacyStore(b.sso) | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	if b.enableAuthZApis { | 
					
						
							|  |  |  | 		// v0alpha1
 | 
					
						
							| 
									
										
										
										
											2025-07-28 19:36:27 +08:00
										 |  |  | 		coreRoleStore, err := NewLocalStore(iamv0.CoreRoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.coreRolesStorage) | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-07-28 19:36:27 +08:00
										 |  |  | 		storage[iamv0.CoreRoleInfo.StoragePath()] = coreRoleStore | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		roleStore, err := NewLocalStore(iamv0.RoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.rolesStorage) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		storage[iamv0.RoleInfo.StoragePath()] = roleStore | 
					
						
							| 
									
										
										
										
											2025-09-29 20:28:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		roleBindingStore, err := NewLocalStore(iamv0.RoleBindingInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.roleBindingsStorage) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		storage[iamv0.RoleBindingInfo.StoragePath()] = roleBindingStore | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-08-27 21:00:09 +08:00
										 |  |  | 	if b.enableResourcePermissionApis { | 
					
						
							|  |  |  | 		resourcePermissionStore, err := NewLocalStore(iamv0.ResourcePermissionInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.resourcePermissionsStorage) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			return err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		storage[iamv0.ResourcePermissionInfo.StoragePath()] = resourcePermissionStore | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	apiGroupInfo.VersionedResourcesStorageMap[legacyiamv0.VERSION] = storage | 
					
						
							| 
									
										
										
										
											2024-09-24 10:07:52 +08:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions { | 
					
						
							| 
									
										
										
										
											2025-07-04 17:07:48 +08:00
										 |  |  | 	return func(rc common.ReferenceCallback) map[string]common.OpenAPIDefinition { | 
					
						
							|  |  |  | 		dst := legacyiamv0.GetOpenAPIDefinitions(rc) | 
					
						
							|  |  |  | 		maps.Copy(dst, iamv0.GetOpenAPIDefinitions(rc)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return dst | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-03 21:40:36 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) { | 
					
						
							|  |  |  | 	oas.Info.Description = "Identity and Access Management" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	defs := b.GetOpenAPIDefinitions()(func(path string) spec.Ref { return spec.Ref{} }) | 
					
						
							|  |  |  | 	defsBase := "github.com/grafana/grafana/pkg/apis/iam/v0alpha1." | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Add missing schemas
 | 
					
						
							|  |  |  | 	for k, v := range defs { | 
					
						
							|  |  |  | 		clean := strings.Replace(k, defsBase, "com.github.grafana.grafana.pkg.apis.iam.v0alpha1.", 1) | 
					
						
							|  |  |  | 		if oas.Components.Schemas[clean] == nil { | 
					
						
							|  |  |  | 			oas.Components.Schemas[clean] = &v.Schema | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	compBase := "com.github.grafana.grafana.pkg.apis.iam.v0alpha1." | 
					
						
							|  |  |  | 	schema := oas.Components.Schemas[compBase+"DisplayList"].Properties["display"] | 
					
						
							|  |  |  | 	schema.Items = &spec.SchemaOrArray{ | 
					
						
							|  |  |  | 		Schema: &spec.Schema{ | 
					
						
							|  |  |  | 			SchemaProps: spec.SchemaProps{ | 
					
						
							|  |  |  | 				AllOf: []spec.Schema{ | 
					
						
							|  |  |  | 					{ | 
					
						
							|  |  |  | 						SchemaProps: spec.SchemaProps{ | 
					
						
							|  |  |  | 							Ref: spec.MustCreateRef("#/components/schemas/" + compBase + "Display"), | 
					
						
							|  |  |  | 						}, | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oas.Components.Schemas[compBase+"DisplayList"].Properties["display"] = schema | 
					
						
							|  |  |  | 	oas.Components.Schemas[compBase+"DisplayList"].Properties["metadata"] = spec.Schema{ | 
					
						
							|  |  |  | 		SchemaProps: spec.SchemaProps{ | 
					
						
							|  |  |  | 			AllOf: []spec.Schema{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					SchemaProps: spec.SchemaProps{ | 
					
						
							|  |  |  | 						Ref: spec.MustCreateRef("#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta"), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	oas.Components.Schemas[compBase+"Display"].Properties["identity"] = spec.Schema{ | 
					
						
							|  |  |  | 		SchemaProps: spec.SchemaProps{ | 
					
						
							|  |  |  | 			AllOf: []spec.Schema{ | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					SchemaProps: spec.SchemaProps{ | 
					
						
							|  |  |  | 						Ref: spec.MustCreateRef("#/components/schemas/" + compBase + "IdentityRef"), | 
					
						
							|  |  |  | 					}, | 
					
						
							|  |  |  | 				}, | 
					
						
							|  |  |  | 			}}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return oas, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-04 01:24:12 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) GetAPIRoutes(gv schema.GroupVersion) *builder.APIRoutes { | 
					
						
							| 
									
										
										
										
											2025-02-03 19:24:35 +08:00
										 |  |  | 	defs := b.GetOpenAPIDefinitions()(func(path string) spec.Ref { return spec.Ref{} }) | 
					
						
							|  |  |  | 	return b.display.GetAPIRoutes(defs) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-05 14:43:54 +08:00
										 |  |  | func (b *IdentityAccessManagementAPIBuilder) GetAuthorizer() authorizer.Authorizer { | 
					
						
							| 
									
										
										
										
											2024-09-23 17:26:44 +08:00
										 |  |  | 	return b.authorizer | 
					
						
							| 
									
										
										
										
											2024-07-26 22:09:08 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | // Validate implements builder.APIGroupValidation.
 | 
					
						
							|  |  |  | // TODO: Move this to the ValidateFunc of the user resource after moving the APIs to use the app-platofrm-sdk.
 | 
					
						
							|  |  |  | // TODO: https://github.com/grafana/grafana/blob/main/apps/playlist/pkg/app/app.go#L62
 | 
					
						
							|  |  |  | func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) { | 
					
						
							|  |  |  | 	switch a.GetOperation() { | 
					
						
							|  |  |  | 	case admission.Create: | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 		switch typedObj := a.GetObject().(type) { | 
					
						
							|  |  |  | 		case *iamv0.User: | 
					
						
							| 
									
										
										
										
											2025-09-19 17:12:00 +08:00
										 |  |  | 			return user.ValidateOnCreate(ctx, typedObj) | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 		case *iamv0.ServiceAccount: | 
					
						
							|  |  |  | 			return serviceaccount.ValidateOnCreate(ctx, typedObj) | 
					
						
							| 
									
										
										
										
											2025-09-18 19:43:07 +08:00
										 |  |  | 		case *iamv0.Team: | 
					
						
							|  |  |  | 			return team.ValidateOnCreate(ctx, typedObj) | 
					
						
							| 
									
										
										
										
											2025-09-26 19:30:28 +08:00
										 |  |  | 		case *iamv0.ResourcePermission: | 
					
						
							|  |  |  | 			return resourcepermission.ValidateCreateAndUpdateInput(ctx, typedObj) | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	case admission.Update: | 
					
						
							| 
									
										
										
										
											2025-09-26 19:30:28 +08:00
										 |  |  | 		switch typedObj := a.GetObject().(type) { | 
					
						
							|  |  |  | 		case *iamv0.ResourcePermission: | 
					
						
							|  |  |  | 			return resourcepermission.ValidateCreateAndUpdateInput(ctx, typedObj) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 	case admission.Delete: | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	case admission.Connect: | 
					
						
							|  |  |  | 		return nil | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Mutate implements builder.APIGroupMutation.
 | 
					
						
							|  |  |  | // TODO: Move this to the MutateFunc of the user resource after moving the APIs to use the app-platofrm-sdk.
 | 
					
						
							|  |  |  | // TODO: https://github.com/grafana/grafana/blob/main/apps/playlist/pkg/app/app.go#L62
 | 
					
						
							|  |  |  | func (b *IdentityAccessManagementAPIBuilder) Mutate(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces) (err error) { | 
					
						
							|  |  |  | 	switch a.GetOperation() { | 
					
						
							|  |  |  | 	case admission.Create: | 
					
						
							| 
									
										
										
										
											2025-09-08 20:31:32 +08:00
										 |  |  | 		switch typedObj := a.GetObject().(type) { | 
					
						
							|  |  |  | 		case *iamv0.User: | 
					
						
							|  |  |  | 			return user.MutateOnCreate(ctx, typedObj) | 
					
						
							|  |  |  | 		case *iamv0.ServiceAccount: | 
					
						
							|  |  |  | 			return serviceaccount.MutateOnCreate(ctx, typedObj) | 
					
						
							| 
									
										
										
										
											2025-07-17 17:50:40 +08:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	case admission.Update: | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	case admission.Delete: | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	case admission.Connect: | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | func NewLocalStore(resourceInfo utils.ResourceInfo, scheme *runtime.Scheme, defaultOptsGetter generic.RESTOptionsGetter, | 
					
						
							|  |  |  | 	reg prometheus.Registerer, ac types.AccessClient, storageBackend resource.StorageBackend) (grafanarest.Storage, error) { | 
					
						
							|  |  |  | 	server, err := resource.NewResourceServer(resource.ResourceServerOptions{ | 
					
						
							|  |  |  | 		Backend:      storageBackend, | 
					
						
							|  |  |  | 		Reg:          reg, | 
					
						
							|  |  |  | 		AccessClient: ac, | 
					
						
							|  |  |  | 	}) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	defaultOpts, err := defaultOptsGetter.GetRESTOptions(resourceInfo.GroupResource(), nil) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	client := resource.NewLocalResourceClient(server) | 
					
						
							| 
									
										
										
										
											2025-08-11 20:22:56 +08:00
										 |  |  | 	optsGetter := apistore.NewRESTOptionsGetterForClient(client, nil, defaultOpts.StorageConfig.Config, nil) | 
					
						
							| 
									
										
										
										
											2025-06-26 16:11:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	store, err := grafanaregistry.NewRegistryStore(scheme, resourceInfo, optsGetter) | 
					
						
							|  |  |  | 	return store, err | 
					
						
							|  |  |  | } |