IAM: Register CoreRole apis (#106924)

* IAM: Register CoreRole apis

* one line store instantiation

* Small refactor for readability

* Add authorizer for CoreRole

* Nit

* Error strings should not end with punctiation

* Account for error

* Switch to use the local resource client

* error should not start with upper casing

* noopStorageErr should have a name starting with err

* Update workspace

* I don't know why I don't have the same output as the CI 🤷

* Dependency xOwnership

* imports

* Import order

* Rename alias to make it clear this is legacy
pull/101501/head^2
Gabriel MABILLE 1 day ago committed by GitHub
parent 6f8e33f360
commit 3d543a336f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 5
      apps/iam/go.mod
  2. 2
      apps/iam/go.sum
  3. 73
      apps/iam/pkg/apis/iam/v0alpha1/register.go
  4. 1
      go.mod
  5. 2
      go.sum
  6. 1
      pkg/apimachinery/identity/context.go
  7. 47
      pkg/registry/apis/iam/authorizer.go
  8. 44
      pkg/registry/apis/iam/models.go
  9. 56
      pkg/registry/apis/iam/noopstorage/storage_backend.go
  10. 128
      pkg/registry/apis/iam/register.go
  11. 8
      pkg/registry/apis/wireset.go
  12. 3
      pkg/services/authz/rbac/mapper.go

@ -4,6 +4,7 @@ go 1.24.4
require (
github.com/grafana/grafana-app-sdk v0.39.0
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e
k8s.io/apimachinery v0.33.1
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff
)
@ -33,11 +34,13 @@ require (
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.64.0 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/rogpeppe/go-internal v1.14.1 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/stretchr/testify v1.10.0 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/otel v1.36.0 // indirect
go.opentelemetry.io/otel/trace v1.36.0 // indirect

@ -36,6 +36,8 @@ github.com/grafana/grafana-app-sdk v0.39.0 h1:WC2E9BKXWDX/e2bajdAFjQEyyWf9BFp7Yz
github.com/grafana/grafana-app-sdk v0.39.0/go.mod h1:xRyBQOttgWTc3tGe9pI0upnpEPVhzALf7Mh/61O4zyY=
github.com/grafana/grafana-app-sdk/logging v0.38.2 h1:EdQTRxbbH72zdqJ09Z76zcSjfALJXkpPLgvKEPPnloc=
github.com/grafana/grafana-app-sdk/logging v0.38.2/go.mod h1:Y/bvbDhBiV/tkIle9RW49pgfSPIPSON8Q4qjx3pyqDk=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e h1:BTKk7LHuG1kmAkucwTA7DuMbKpKvJTKrGdBmUNO4dfQ=
github.com/grafana/grafana/pkg/apimachinery v0.0.0-20250514132646-acbc7b54ed9e/go.mod h1:IA4SOwun8QyST9c5UNs/fN37XL6boXXDvRYFcFwbipg=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=

@ -0,0 +1,73 @@
package v0alpha1
import (
"fmt"
"time"
"github.com/grafana/grafana/pkg/apimachinery/utils"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
const (
GROUP = "iam.grafana.app"
VERSION = "v0alpha1"
APIVERSION = GROUP + "/" + VERSION
)
var CoreRoleInfo = utils.NewResourceInfo(GROUP, VERSION,
"coreroles", "corerole", "CoreRole",
func() runtime.Object { return &CoreRole{} },
func() runtime.Object { return &CoreRoleList{} },
utils.TableColumns{
Definition: []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name"},
{Name: "Title", Type: "string", Format: "string", Description: "Core role name"}, // Not sure this is actually needed
{Name: "Created At", Type: "date"},
},
Reader: func(obj any) ([]interface{}, error) {
core, ok := obj.(*CoreRole)
if ok {
if core != nil {
return []interface{}{
core.Name,
core.Spec.Title,
core.CreationTimestamp.UTC().Format(time.RFC3339),
}, nil
}
}
return nil, fmt.Errorf("expected core role")
},
},
)
var (
SchemeBuilder runtime.SchemeBuilder
localSchemeBuilder = &SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
schemeGroupVersion = schema.GroupVersion{Group: GROUP, Version: VERSION}
)
func init() {
localSchemeBuilder.Register(addKnownTypes, addDefaultingFuncs)
}
// Adds the list of known types to the given scheme.
func addKnownTypes(scheme *runtime.Scheme) error {
scheme.AddKnownTypes(schemeGroupVersion,
&CoreRole{},
&CoreRoleList{},
// What is this about?
&metav1.PartialObjectMetadata{},
&metav1.PartialObjectMetadataList{},
)
metav1.AddToGroupVersion(scheme, schemeGroupVersion)
return nil
}
func addDefaultingFuncs(scheme *runtime.Scheme) error {
// return RegisterDefaults(scheme)
return nil
}

@ -212,6 +212,7 @@ require (
github.com/grafana/grafana/apps/alerting/notifications v0.0.0-20250527064921-326081cdb7a1 // @grafana/alerting-backend
github.com/grafana/grafana/apps/dashboard v0.0.0-20250616215703-eb4de388c3cf // @grafana/grafana-app-platform-squad @grafana/dashboards-squad
github.com/grafana/grafana/apps/folder v0.0.0-20250527064921-326081cdb7a1 // @grafana/grafana-search-and-storage
github.com/grafana/grafana/apps/iam v0.0.0-20250620082852-de59956db4df // @grafana/identity-access-team
github.com/grafana/grafana/apps/investigations v0.0.0-20250527064921-326081cdb7a1 // @fcjack @matryer
github.com/grafana/grafana/apps/playlist v0.0.0-20250527064921-326081cdb7a1 // @grafana/grafana-app-platform-squad
github.com/grafana/grafana/pkg/aggregator v0.0.0-20250527064921-326081cdb7a1 // @grafana/grafana-app-platform-squad

@ -1621,6 +1621,8 @@ github.com/grafana/grafana/apps/dashboard v0.0.0-20250616215703-eb4de388c3cf h1:
github.com/grafana/grafana/apps/dashboard v0.0.0-20250616215703-eb4de388c3cf/go.mod h1:XcJIUxPfKAhDb9mx9jHeV9U2xVA2WgE93ukd4Bx8U/M=
github.com/grafana/grafana/apps/folder v0.0.0-20250527064921-326081cdb7a1 h1:SkVUcSlXIUi46gSGfe5/Xqn2p51c/unmkZRDX+274Os=
github.com/grafana/grafana/apps/folder v0.0.0-20250527064921-326081cdb7a1/go.mod h1:z9u5VFG9q1CcIt62c9RIP8dEWHX81NdiPCjtpKKeuFU=
github.com/grafana/grafana/apps/iam v0.0.0-20250620082852-de59956db4df h1:t0y8BiEJ7dBq2/eYQI66kYyOGYdPCxNPNftrRwL6kIo=
github.com/grafana/grafana/apps/iam v0.0.0-20250620082852-de59956db4df/go.mod h1:6j/luBu5kqJysChmy9bnlCDYvukIAxqaoUEVI+HaCuA=
github.com/grafana/grafana/apps/investigations v0.0.0-20250527064921-326081cdb7a1 h1:IN+KiBwtvJ3JCLdsPyAmGKJupWckH3h5iXpELd78rPo=
github.com/grafana/grafana/apps/investigations v0.0.0-20250527064921-326081cdb7a1/go.mod h1:/uuCYrNS3VOTZ7gTD+cBHD9WBIYhqj7bK6EnX3TP1wg=
github.com/grafana/grafana/apps/playlist v0.0.0-20250527064921-326081cdb7a1 h1:GNPPHXhUv++4qKdrUJg/Mfi/HJPGlc5u+/FjElYAcOM=

@ -125,6 +125,7 @@ var serviceIdentityTokenPermissions = getTokenPermissions(
"dashboard.grafana.app",
"secret.grafana.app",
"query.grafana.app",
"iam.grafana.app",
)
var ServiceIdentityClaims = &authn.Claims[authn.AccessTokenClaims]{

@ -7,18 +7,53 @@ import (
"k8s.io/apiserver/pkg/authorization/authorizer"
authlib "github.com/grafana/authlib/types"
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/utils"
iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1"
legacyiamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
"github.com/grafana/grafana/pkg/services/accesscontrol"
gfauthorizer "github.com/grafana/grafana/pkg/services/apiserver/auth/authorizer"
)
func newLegacyAuthorizer(ac accesscontrol.AccessControl, store legacy.LegacyIdentityStore) (authorizer.Authorizer, authlib.AccessClient) {
type iamAuthorizer struct {
resourceAuthorizer map[string]authorizer.Authorizer // Map resource to its authorizer
}
func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient authlib.AccessClient) authorizer.Authorizer {
resourceAuthorizer := make(map[string]authorizer.Authorizer)
// Identity specific resources
legacyAuthorizer := gfauthorizer.NewResourceAuthorizer(legacyAccessClient)
resourceAuthorizer[legacyiamv0.UserResourceInfo.GetName()] = legacyAuthorizer
resourceAuthorizer[legacyiamv0.ServiceAccountResourceInfo.GetName()] = legacyAuthorizer
resourceAuthorizer[legacyiamv0.TeamResourceInfo.GetName()] = legacyAuthorizer
resourceAuthorizer["display"] = legacyAuthorizer
// Access specific resources
authorizer := gfauthorizer.NewResourceAuthorizer(accessClient)
resourceAuthorizer[iamv0.CoreRoleInfo.GetName()] = authorizer
return &iamAuthorizer{resourceAuthorizer: resourceAuthorizer}
}
func (s *iamAuthorizer) Authorize(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
if !attr.IsResourceRequest() {
return authorizer.DecisionNoOpinion, "", nil
}
authz, ok := s.resourceAuthorizer[attr.GetResource()]
if !ok {
return authorizer.DecisionDeny, "", fmt.Errorf("no authorizer found for resource %s", attr.GetResource())
}
return authz.Authorize(ctx, attr)
}
func newLegacyAccessClient(ac accesscontrol.AccessControl, store legacy.LegacyIdentityStore) authlib.AccessClient {
client := accesscontrol.NewLegacyAccessClient(
ac,
accesscontrol.ResourceAuthorizerOptions{
Resource: iamv0.UserResourceInfo.GetName(),
Resource: legacyiamv0.UserResourceInfo.GetName(),
Attr: "id",
Mapping: map[string]string{
utils.VerbGet: accesscontrol.ActionOrgUsersRead,
@ -42,7 +77,7 @@ func newLegacyAuthorizer(ac accesscontrol.AccessControl, store legacy.LegacyIden
},
},
accesscontrol.ResourceAuthorizerOptions{
Resource: iamv0.ServiceAccountResourceInfo.GetName(),
Resource: legacyiamv0.ServiceAccountResourceInfo.GetName(),
Attr: "id",
Resolver: accesscontrol.ResourceResolverFunc(func(ctx context.Context, ns authlib.NamespaceInfo, name string) ([]string, error) {
res, err := store.GetServiceAccountInternalID(ctx, ns, legacy.GetServiceAccountInternalIDQuery{
@ -55,7 +90,7 @@ func newLegacyAuthorizer(ac accesscontrol.AccessControl, store legacy.LegacyIden
}),
},
accesscontrol.ResourceAuthorizerOptions{
Resource: iamv0.TeamResourceInfo.GetName(),
Resource: legacyiamv0.TeamResourceInfo.GetName(),
Attr: "id",
Resolver: accesscontrol.ResourceResolverFunc(func(ctx context.Context, ns authlib.NamespaceInfo, name string) ([]string, error) {
res, err := store.GetTeamInternalID(ctx, ns, legacy.GetTeamInternalIDQuery{
@ -69,5 +104,5 @@ func newLegacyAuthorizer(ac accesscontrol.AccessControl, store legacy.LegacyIden
},
)
return gfauthorizer.NewResourceAuthorizer(client), client
return client
}

@ -0,0 +1,44 @@
package iam
import (
"github.com/grafana/authlib/types"
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
"github.com/grafana/grafana/pkg/registry/apis/iam/user"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/ssosettings"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apiserver/pkg/authorization/authorizer"
)
var _ builder.APIGroupBuilder = (*IdentityAccessManagementAPIBuilder)(nil)
var _ builder.APIGroupRouteProvider = (*IdentityAccessManagementAPIBuilder)(nil)
// CoreRoleStorageBackend uses the resource.StorageBackend interface to provide storage for core roles.
// Used wire to identify the storage backend for core roles.
type CoreRoleStorageBackend interface{ resource.StorageBackend }
// This is used just so wire has something unique to return
type IdentityAccessManagementAPIBuilder struct {
// Stores
store legacy.LegacyIdentityStore
coreRolesStorage CoreRoleStorageBackend
// Access Control
authorizer authorizer.Authorizer
// legacyAccessClient is used for the identity apis, we need to migrate to the access client
legacyAccessClient types.AccessClient
// accessClient is used for the core role apis
accessClient types.AccessClient
reg prometheus.Registerer
// non-k8s api route
display *user.LegacyDisplayREST
// Not set for multi-tenant deployment for now
sso ssosettings.Service
// Toggle for enabling authz management apis
enableAuthZApis bool
}

@ -0,0 +1,56 @@
package noopstorage
import (
"context"
"errors"
"net/http"
"github.com/grafana/grafana/pkg/storage/unified/resource"
"github.com/grafana/grafana/pkg/storage/unified/resourcepb"
)
var (
_ resource.StorageBackend = &StorageBackendImpl{}
errNoopStorage = errors.New("unavailable functionality")
)
type StorageBackendImpl struct{}
func ProvideStorageBackend() *StorageBackendImpl {
return &StorageBackendImpl{}
}
// GetResourceStats implements resource.StorageBackend.
func (c *StorageBackendImpl) GetResourceStats(ctx context.Context, namespace string, minCount int) ([]resource.ResourceStats, error) {
return []resource.ResourceStats{}, errNoopStorage
}
// ListHistory implements resource.StorageBackend.
func (c *StorageBackendImpl) ListHistory(context.Context, *resourcepb.ListRequest, func(resource.ListIterator) error) (int64, error) {
return 0, errNoopStorage
}
// ListIterator implements resource.StorageBackend.
func (c *StorageBackendImpl) ListIterator(context.Context, *resourcepb.ListRequest, func(resource.ListIterator) error) (int64, error) {
return 0, errNoopStorage
}
// ReadResource implements resource.StorageBackend.
func (c *StorageBackendImpl) ReadResource(_ context.Context, req *resourcepb.ReadRequest) *resource.BackendReadResponse {
return &resource.BackendReadResponse{
Key: req.GetKey(),
Error: &resourcepb.ErrorResult{Code: http.StatusForbidden, Message: errNoopStorage.Error()},
}
}
// WatchWriteEvents implements resource.StorageBackend.
func (c *StorageBackendImpl) WatchWriteEvents(ctx context.Context) (<-chan *resource.WrittenEvent, error) {
stream := make(chan *resource.WrittenEvent, 10)
return stream, nil
}
// WriteEvent implements resource.StorageBackend.
func (c *StorageBackendImpl) WriteEvent(context.Context, resource.WriteEvent) (int64, error) {
return 0, errNoopStorage
}

@ -4,10 +4,12 @@ import (
"context"
"strings"
"github.com/prometheus/client_golang/prometheus"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apiserver/pkg/authorization/authorizer"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
common "k8s.io/kube-openapi/pkg/common"
@ -15,8 +17,12 @@ import (
"k8s.io/kube-openapi/pkg/validation/spec"
"github.com/grafana/authlib/types"
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
"github.com/grafana/grafana/pkg/apimachinery/identity"
iamv0 "github.com/grafana/grafana/pkg/apis/iam/v0alpha1"
"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"
"github.com/grafana/grafana/pkg/infra/db"
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
"github.com/grafana/grafana/pkg/registry/apis/iam/serviceaccount"
@ -25,41 +31,37 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/iam/user"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/apiserver/builder"
"github.com/grafana/grafana/pkg/services/featuremgmt"
"github.com/grafana/grafana/pkg/services/ssosettings"
"github.com/grafana/grafana/pkg/storage/legacysql"
"github.com/grafana/grafana/pkg/storage/unified/apistore"
"github.com/grafana/grafana/pkg/storage/unified/resource"
)
var _ builder.APIGroupBuilder = (*IdentityAccessManagementAPIBuilder)(nil)
var _ builder.APIGroupRouteProvider = (*IdentityAccessManagementAPIBuilder)(nil)
// This is used just so wire has something unique to return
type IdentityAccessManagementAPIBuilder struct {
store legacy.LegacyIdentityStore
authorizer authorizer.Authorizer
accessClient types.AccessClient
// non-k8s api route
display *user.LegacyDisplayREST
// Not set for multi-tenant deployment for now
sso ssosettings.Service
}
func RegisterAPIService(
features featuremgmt.FeatureToggles,
apiregistration builder.APIRegistrar,
ssoService ssosettings.Service,
sql db.DB,
ac accesscontrol.AccessControl,
accessClient types.AccessClient,
reg prometheus.Registerer,
coreRolesStorage CoreRoleStorageBackend,
) (*IdentityAccessManagementAPIBuilder, error) {
store := legacy.NewLegacySQLStores(legacysql.NewDatabaseProvider(sql))
authorizer, client := newLegacyAuthorizer(ac, store)
legacyAccessClient := newLegacyAccessClient(ac, store)
authorizer := newIAMAuthorizer(accessClient, legacyAccessClient)
builder := &IdentityAccessManagementAPIBuilder{
store: store,
sso: ssoService,
authorizer: authorizer,
accessClient: client,
display: user.NewLegacyDisplayREST(store),
store: store,
coreRolesStorage: coreRolesStorage,
sso: ssoService,
authorizer: authorizer,
legacyAccessClient: legacyAccessClient,
accessClient: accessClient,
display: user.NewLegacyDisplayREST(store),
reg: reg,
enableAuthZApis: features.IsEnabledGlobally(featuremgmt.FlagKubernetesAuthzApis),
}
apiregistration.RegisterAPI(builder)
@ -85,54 +87,80 @@ func NewAPIService(store legacy.LegacyIdentityStore) *IdentityAccessManagementAP
}
func (b *IdentityAccessManagementAPIBuilder) GetGroupVersion() schema.GroupVersion {
return iamv0.SchemeGroupVersion
return legacyiamv0.SchemeGroupVersion
}
func (b *IdentityAccessManagementAPIBuilder) InstallSchema(scheme *runtime.Scheme) error {
iamv0.AddKnownTypes(scheme, iamv0.VERSION)
if b.enableAuthZApis {
if err := iamv0.AddToScheme(scheme); err != nil {
return err
}
}
legacyiamv0.AddKnownTypes(scheme, legacyiamv0.VERSION)
// Link this version to the internal representation.
// This is used for server-side-apply (PATCH), and avoids the error:
// "no kind is registered for the type"
iamv0.AddKnownTypes(scheme, runtime.APIVersionInternal)
legacyiamv0.AddKnownTypes(scheme, runtime.APIVersionInternal)
metav1.AddToGroupVersion(scheme, iamv0.SchemeGroupVersion)
return scheme.SetVersionPriority(iamv0.SchemeGroupVersion)
metav1.AddToGroupVersion(scheme, legacyiamv0.SchemeGroupVersion)
return scheme.SetVersionPriority(legacyiamv0.SchemeGroupVersion)
}
func (b *IdentityAccessManagementAPIBuilder) AllowedV0Alpha1Resources() []string {
return []string{builder.AllResourcesAllowed}
}
func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, _ builder.APIGroupOptions) error {
func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.APIGroupInfo, opts builder.APIGroupOptions) error {
storage := map[string]rest.Storage{}
teamResource := iamv0.TeamResourceInfo
storage[teamResource.StoragePath()] = team.NewLegacyStore(b.store, b.accessClient)
teamResource := legacyiamv0.TeamResourceInfo
storage[teamResource.StoragePath()] = team.NewLegacyStore(b.store, b.legacyAccessClient)
storage[teamResource.StoragePath("members")] = team.NewLegacyTeamMemberREST(b.store)
teamBindingResource := iamv0.TeamBindingResourceInfo
teamBindingResource := legacyiamv0.TeamBindingResourceInfo
storage[teamBindingResource.StoragePath()] = team.NewLegacyBindingStore(b.store)
userResource := iamv0.UserResourceInfo
storage[userResource.StoragePath()] = user.NewLegacyStore(b.store, b.accessClient)
userResource := legacyiamv0.UserResourceInfo
storage[userResource.StoragePath()] = user.NewLegacyStore(b.store, b.legacyAccessClient)
storage[userResource.StoragePath("teams")] = user.NewLegacyTeamMemberREST(b.store)
serviceAccountResource := iamv0.ServiceAccountResourceInfo
storage[serviceAccountResource.StoragePath()] = serviceaccount.NewLegacyStore(b.store, b.accessClient)
serviceAccountResource := legacyiamv0.ServiceAccountResourceInfo
storage[serviceAccountResource.StoragePath()] = serviceaccount.NewLegacyStore(b.store, b.legacyAccessClient)
storage[serviceAccountResource.StoragePath("tokens")] = serviceaccount.NewLegacyTokenREST(b.store)
if b.sso != nil {
ssoResource := iamv0.SSOSettingResourceInfo
ssoResource := legacyiamv0.SSOSettingResourceInfo
storage[ssoResource.StoragePath()] = sso.NewLegacyStore(b.sso)
}
apiGroupInfo.VersionedResourcesStorageMap[iamv0.VERSION] = storage
if b.enableAuthZApis {
// v0alpha1
store, err := NewLocalStore(iamv0.CoreRoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.coreRolesStorage)
if err != nil {
return err
}
storage[iamv0.CoreRoleInfo.StoragePath()] = store
}
apiGroupInfo.VersionedResourcesStorageMap[legacyiamv0.VERSION] = storage
return nil
}
func (b *IdentityAccessManagementAPIBuilder) GetOpenAPIDefinitions() common.GetOpenAPIDefinitions {
return iamv0.GetOpenAPIDefinitions
defs := legacyiamv0.GetOpenAPIDefinitions
if b.enableAuthZApis {
defs = func(ref common.ReferenceCallback) map[string]common.OpenAPIDefinition {
def1 := legacyiamv0.GetOpenAPIDefinitions(ref)
def2 := iamv0.GetOpenAPIDefinitions(ref)
for k, v := range def2 {
def1[k] = v
}
return def1
}
}
return defs
}
func (b *IdentityAccessManagementAPIBuilder) PostProcessOpenAPI(oas *spec3.OpenAPI) (*spec3.OpenAPI, error) {
@ -195,3 +223,25 @@ func (b *IdentityAccessManagementAPIBuilder) GetAPIRoutes(gv schema.GroupVersion
func (b *IdentityAccessManagementAPIBuilder) GetAuthorizer() authorizer.Authorizer {
return b.authorizer
}
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)
optsGetter := apistore.NewRESTOptionsGetterForClient(client, defaultOpts.StorageConfig.Config, nil)
store, err := grafanaregistry.NewRegistryStore(scheme, resourceInfo, optsGetter)
return store, err
}

@ -9,6 +9,7 @@ import (
"github.com/grafana/grafana/pkg/registry/apis/featuretoggle"
"github.com/grafana/grafana/pkg/registry/apis/folders"
"github.com/grafana/grafana/pkg/registry/apis/iam"
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
"github.com/grafana/grafana/pkg/registry/apis/provisioning"
"github.com/grafana/grafana/pkg/registry/apis/provisioning/webhooks"
"github.com/grafana/grafana/pkg/registry/apis/query"
@ -30,6 +31,12 @@ var ProvisioningExtras = wire.NewSet(
MergeProvisioningExtras,
)
// WireSetExts is a set of providers that can be overridden by enterprise implementations.
var WireSetExts = wire.NewSet(
noopstorage.ProvideStorageBackend,
wire.Bind(new(iam.CoreRoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
)
var WireSet = wire.NewSet(
ProvideRegistryServiceSink, // dummy background service that forces registration
@ -44,6 +51,7 @@ var WireSet = wire.NewSet(
featuretoggle.RegisterAPIService,
datasource.RegisterAPIService,
folders.RegisterAPIService,
WireSetExts, // this will be moved to wireexts_oss.go in a following PR
iam.RegisterAPIService,
ProvisioningExtras,
provisioning.RegisterAPIService,

@ -102,7 +102,8 @@ func NewMapperRegistry() MapperRegistry {
"folders": newResourceTranslation("folders", "uid", true),
},
"iam.grafana.app": {
"teams": newResourceTranslation("teams", "id", false),
"teams": newResourceTranslation("teams", "id", false),
"coreroles": newResourceTranslation("roles", "uid", false),
},
"secret.grafana.app": {
"securevalues": newResourceTranslation("secret.securevalues", "uid", false),

Loading…
Cancel
Save