grafana-iam: Use an API Installer interface (#115310)
* `grafana-iam`: Add basic roles to the apis * Fix validation * chore: trigger CI * Leave the hooks intact for now, moving them later * Remove Role mention from the interface * Refactor to use a NoopRest backend and Deny access
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
package iam
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/authorization/authorizer"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
|
||||
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/noopstorage"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/builder"
|
||||
)
|
||||
|
||||
// ApiInstaller is a generic interface for installing API resources.
|
||||
// This allows different implementations (OSS noop, Enterprise with features)
|
||||
// to provide their own resource registration and validation logic.
|
||||
type ApiInstaller[T runtime.Object] interface {
|
||||
// GetAuthorizer returns the authorizer for the API group.
|
||||
GetAuthorizer() authorizer.Authorizer
|
||||
|
||||
// RegisterStorage registers storage and hooks with the API group.
|
||||
RegisterStorage(apiGroupInfo *server.APIGroupInfo, opts *builder.APIGroupOptions, storage map[string]rest.Storage) error
|
||||
|
||||
// ValidateOnCreate validates object creation.
|
||||
ValidateOnCreate(ctx context.Context, obj T) error
|
||||
|
||||
// ValidateOnUpdate validates object updates.
|
||||
ValidateOnUpdate(ctx context.Context, oldObj, newObj T) error
|
||||
|
||||
// ValidateOnDelete validates object deletion.
|
||||
ValidateOnDelete(ctx context.Context, obj T) error
|
||||
}
|
||||
|
||||
// RoleApiInstaller provides Role-specific API registration and validation.
|
||||
// This interface allows enterprise implementations to provide custom role handling
|
||||
// while keeping the core IAM registration logic in OSS.
|
||||
type RoleApiInstaller ApiInstaller[*iamv0.Role]
|
||||
|
||||
// NoopApiInstaller is a no-op implementation for when roles are not available (OSS).
|
||||
type NoopApiInstaller[T runtime.Object] struct {
|
||||
ResourceInfo utils.ResourceInfo
|
||||
}
|
||||
|
||||
func (n *NoopApiInstaller[T]) GetAuthorizer() authorizer.Authorizer {
|
||||
return authorizer.AuthorizerFunc(func(ctx context.Context, attr authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
return authorizer.DecisionDeny, "Unavailable functionality", nil
|
||||
})
|
||||
}
|
||||
|
||||
func (n *NoopApiInstaller[T]) RegisterStorage(apiGroupInfo *server.APIGroupInfo, opts *builder.APIGroupOptions, storage map[string]rest.Storage) error {
|
||||
storage[n.ResourceInfo.StoragePath()] = &noopstorage.NoopREST{ResourceInfo: n.ResourceInfo}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NoopApiInstaller[T]) ValidateOnCreate(ctx context.Context, obj T) error {
|
||||
// No validation needed in OSS
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NoopApiInstaller[T]) ValidateOnUpdate(ctx context.Context, oldObj, newObj T) error {
|
||||
// No validation needed in OSS
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NoopApiInstaller[T]) ValidateOnDelete(ctx context.Context, obj T) error {
|
||||
// No validation needed in OSS
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProvideNoopRoleApiInstaller provides a no-op role installer specifically for Role types.
|
||||
// This is needed for Wire dependency injection which doesn't handle generic functions well.
|
||||
func ProvideNoopRoleApiInstaller() RoleApiInstaller {
|
||||
return &NoopApiInstaller[*iamv0.Role]{
|
||||
ResourceInfo: iamv0.RoleInfo,
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,11 @@ type iamAuthorizer struct {
|
||||
resourceAuthorizer map[string]authorizer.Authorizer // Map resource to its authorizer
|
||||
}
|
||||
|
||||
func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient authlib.AccessClient) authorizer.Authorizer {
|
||||
func newIAMAuthorizer(
|
||||
accessClient authlib.AccessClient,
|
||||
legacyAccessClient authlib.AccessClient,
|
||||
roleApiInstaller RoleApiInstaller,
|
||||
) authorizer.Authorizer {
|
||||
resourceAuthorizer := make(map[string]authorizer.Authorizer)
|
||||
|
||||
serviceAuthorizer := gfauthorizer.NewServiceAuthorizer()
|
||||
@@ -44,7 +48,7 @@ func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient auth
|
||||
// Access specific resources
|
||||
authorizer := gfauthorizer.NewResourceAuthorizer(accessClient)
|
||||
resourceAuthorizer[iamv0.CoreRoleInfo.GetName()] = iamauthorizer.NewCoreRoleAuthorizer(accessClient)
|
||||
resourceAuthorizer[iamv0.RoleInfo.GetName()] = authorizer
|
||||
resourceAuthorizer[iamv0.RoleInfo.GetName()] = roleApiInstaller.GetAuthorizer()
|
||||
resourceAuthorizer[iamv0.ResourcePermissionInfo.GetName()] = allowAuthorizer // Handled by the backend wrapper
|
||||
resourceAuthorizer[iamv0.RoleBindingInfo.GetName()] = authorizer
|
||||
resourceAuthorizer[iamv0.ServiceAccountResourceInfo.GetName()] = authorizer
|
||||
|
||||
@@ -56,7 +56,7 @@ type IdentityAccessManagementAPIBuilder struct {
|
||||
teamBindingLegacyStore *teambinding.LegacyBindingStore
|
||||
ssoLegacyStore *sso.LegacyStore
|
||||
coreRolesStorage CoreRoleStorageBackend
|
||||
rolesStorage RoleStorageBackend
|
||||
roleApiInstaller RoleApiInstaller
|
||||
resourcePermissionsStorage resource.StorageBackend
|
||||
roleBindingsStorage RoleBindingStorageBackend
|
||||
externalGroupMappingStorage ExternalGroupMappingStorageBackend
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
package noopstorage
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/apis/meta/internalversion"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/watch"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
)
|
||||
|
||||
type NoopREST struct {
|
||||
ResourceInfo utils.ResourceInfo
|
||||
}
|
||||
|
||||
var (
|
||||
_ rest.Storage = (*NoopREST)(nil)
|
||||
_ rest.Scoper = (*NoopREST)(nil)
|
||||
_ rest.SingularNameProvider = (*NoopREST)(nil)
|
||||
_ rest.Getter = (*NoopREST)(nil)
|
||||
)
|
||||
|
||||
func (n *NoopREST) New() runtime.Object {
|
||||
return n.ResourceInfo.NewFunc()
|
||||
}
|
||||
|
||||
func (n *NoopREST) NewList() runtime.Object {
|
||||
return n.ResourceInfo.NewListFunc()
|
||||
}
|
||||
|
||||
func (n *NoopREST) NamespaceScoped() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (n *NoopREST) GetSingularName() string {
|
||||
return n.ResourceInfo.GetSingularName()
|
||||
}
|
||||
|
||||
func (n *NoopREST) Destroy() {}
|
||||
|
||||
func (n *NoopREST) Get(ctx context.Context, name string, options *metav1.GetOptions) (runtime.Object, error) {
|
||||
return nil, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) List(ctx context.Context, options *internalversion.ListOptions) (runtime.Object, error) {
|
||||
return nil, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) Create(ctx context.Context, obj runtime.Object, createValidation rest.ValidateObjectFunc, options *metav1.CreateOptions) (runtime.Object, error) {
|
||||
return nil, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) Update(ctx context.Context, name string, objInfo rest.UpdatedObjectInfo, createValidation rest.ValidateObjectFunc, updateValidation rest.ValidateObjectUpdateFunc, forceAllowCreate bool, options *metav1.UpdateOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) Delete(ctx context.Context, name string, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions) (runtime.Object, bool, error) {
|
||||
return nil, false, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) DeleteCollection(ctx context.Context, deleteValidation rest.ValidateObjectFunc, options *metav1.DeleteOptions, listOptions *internalversion.ListOptions) (runtime.Object, error) {
|
||||
return nil, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) Watch(ctx context.Context, options *internalversion.ListOptions) (watch.Interface, error) {
|
||||
return nil, errNoopStorage
|
||||
}
|
||||
|
||||
func (n *NoopREST) ConvertToTable(ctx context.Context, object runtime.Object, tableOptions runtime.Object) (*metav1.Table, error) {
|
||||
return nil, errNoopStorage
|
||||
}
|
||||
@@ -72,7 +72,7 @@ func RegisterAPIService(
|
||||
zClient zanzana.Client,
|
||||
reg prometheus.Registerer,
|
||||
coreRolesStorage CoreRoleStorageBackend,
|
||||
rolesStorage RoleStorageBackend,
|
||||
roleApiInstaller RoleApiInstaller,
|
||||
tracing *tracing.TracingService,
|
||||
roleBindingsStorage RoleBindingStorageBackend,
|
||||
externalGroupMappingStorageBackend ExternalGroupMappingStorageBackend,
|
||||
@@ -87,7 +87,7 @@ func RegisterAPIService(
|
||||
dbProvider := legacysql.NewDatabaseProvider(sql)
|
||||
store := legacy.NewLegacySQLStores(dbProvider)
|
||||
legacyAccessClient := newLegacyAccessClient(ac, store)
|
||||
authorizer := newIAMAuthorizer(accessClient, legacyAccessClient)
|
||||
authorizer := newIAMAuthorizer(accessClient, legacyAccessClient, roleApiInstaller)
|
||||
registerMetrics(reg)
|
||||
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
@@ -106,7 +106,7 @@ func RegisterAPIService(
|
||||
teamBindingLegacyStore: teambinding.NewLegacyBindingStore(store, enableAuthnMutation, tracing),
|
||||
ssoLegacyStore: sso.NewLegacyStore(ssoService, tracing),
|
||||
coreRolesStorage: coreRolesStorage,
|
||||
rolesStorage: rolesStorage,
|
||||
roleApiInstaller: roleApiInstaller,
|
||||
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
|
||||
roleBindingsStorage: roleBindingsStorage,
|
||||
externalGroupMappingStorage: externalGroupMappingStorageBackend,
|
||||
@@ -139,7 +139,7 @@ func NewAPIService(
|
||||
accessClient types.AccessClient,
|
||||
dbProvider legacysql.LegacyDatabaseProvider,
|
||||
coreRoleStorage CoreRoleStorageBackend,
|
||||
roleStorage RoleStorageBackend,
|
||||
roleApiInstaller RoleApiInstaller,
|
||||
features featuremgmt.FeatureToggles,
|
||||
zClient zanzana.Client,
|
||||
reg prometheus.Registerer,
|
||||
@@ -162,7 +162,6 @@ func NewAPIService(
|
||||
store: store,
|
||||
display: user.NewLegacyDisplayREST(store),
|
||||
resourcePermissionsStorage: resourcePermissionsStorage,
|
||||
rolesStorage: roleStorage,
|
||||
coreRolesStorage: coreRoleStorage,
|
||||
roleBindingsStorage: noopstorage.ProvideStorageBackend(), // TODO: add a proper storage backend
|
||||
logger: log.New("iam.apis"),
|
||||
@@ -172,6 +171,7 @@ func NewAPIService(
|
||||
zClient: zClient,
|
||||
zTickets: make(chan bool, MaxConcurrentZanzanaWrites),
|
||||
reg: reg,
|
||||
roleApiInstaller: roleApiInstaller,
|
||||
authorizer: authorizer.AuthorizerFunc(
|
||||
func(ctx context.Context, a authorizer.Attributes) (authorizer.Decision, string, error) {
|
||||
user, ok := types.AuthInfoFrom(ctx)
|
||||
@@ -377,23 +377,17 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
|
||||
}
|
||||
if enableZanzanaSync {
|
||||
b.logger.Info("Enabling hooks for CoreRole to sync to Zanzana")
|
||||
coreRoleStore.AfterCreate = b.AfterRoleCreate
|
||||
coreRoleStore.AfterDelete = b.AfterRoleDelete
|
||||
coreRoleStore.BeginUpdate = b.BeginRoleUpdate
|
||||
h := NewRoleHooks(b.zClient, b.zTickets, b.logger)
|
||||
coreRoleStore.AfterCreate = h.AfterRoleCreate
|
||||
coreRoleStore.AfterDelete = h.AfterRoleDelete
|
||||
coreRoleStore.BeginUpdate = h.BeginRoleUpdate
|
||||
}
|
||||
storage[iamv0.CoreRoleInfo.StoragePath()] = coreRoleStore
|
||||
|
||||
roleStore, err := NewLocalStore(iamv0.RoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.rolesStorage)
|
||||
if err != nil {
|
||||
// Role registration is delegated to the RoleApiInstaller
|
||||
if err := b.roleApiInstaller.RegisterStorage(apiGroupInfo, &opts, storage); err != nil {
|
||||
return err
|
||||
}
|
||||
if enableZanzanaSync {
|
||||
b.logger.Info("Enabling hooks for Role to sync to Zanzana")
|
||||
roleStore.AfterCreate = b.AfterRoleCreate
|
||||
roleStore.AfterDelete = b.AfterRoleDelete
|
||||
roleStore.BeginUpdate = b.BeginRoleUpdate
|
||||
}
|
||||
storage[iamv0.RoleInfo.StoragePath()] = roleStore
|
||||
|
||||
roleBindingStore, err := NewLocalStore(iamv0.RoleBindingInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.roleBindingsStorage)
|
||||
if err != nil {
|
||||
@@ -568,6 +562,8 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
|
||||
return resourcepermission.ValidateCreateAndUpdateInput(ctx, typedObj)
|
||||
case *iamv0.ExternalGroupMapping:
|
||||
return externalgroupmapping.ValidateOnCreate(typedObj)
|
||||
case *iamv0.Role:
|
||||
return b.roleApiInstaller.ValidateOnCreate(ctx, typedObj)
|
||||
}
|
||||
return nil
|
||||
case admission.Update:
|
||||
@@ -592,9 +588,19 @@ func (b *IdentityAccessManagementAPIBuilder) Validate(ctx context.Context, a adm
|
||||
return fmt.Errorf("expected old object to be a TeamBinding, got %T", oldTeamBindingObj)
|
||||
}
|
||||
return teambinding.ValidateOnUpdate(ctx, typedObj, oldTeamBindingObj)
|
||||
case *iamv0.Role:
|
||||
oldRoleObj, ok := a.GetOldObject().(*iamv0.Role)
|
||||
if !ok {
|
||||
return fmt.Errorf("expected old object to be a Role, got %T", oldRoleObj)
|
||||
}
|
||||
return b.roleApiInstaller.ValidateOnUpdate(ctx, oldRoleObj, typedObj)
|
||||
}
|
||||
return nil
|
||||
case admission.Delete:
|
||||
switch oldRoleObj := a.GetOldObject().(type) {
|
||||
case *iamv0.Role:
|
||||
return b.roleApiInstaller.ValidateOnDelete(ctx, oldRoleObj)
|
||||
}
|
||||
return nil
|
||||
case admission.Connect:
|
||||
return nil
|
||||
|
||||
@@ -9,12 +9,23 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/generic/registry"
|
||||
|
||||
iamv0 "github.com/grafana/grafana/apps/iam/pkg/apis/iam/v0alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
v1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
||||
)
|
||||
|
||||
type RoleHooks struct {
|
||||
zClient zanzana.Client
|
||||
zTickets chan bool
|
||||
logger log.Logger
|
||||
}
|
||||
|
||||
func NewRoleHooks(zClient zanzana.Client, zTickets chan bool, logger log.Logger) *RoleHooks {
|
||||
return &RoleHooks{zClient: zClient, zTickets: zTickets, logger: logger}
|
||||
}
|
||||
|
||||
// convertRolePermissionsToTuples converts role permissions (action/scope) to v1 TupleKey format
|
||||
// using the shared zanzana.ConvertRolePermissionsToTuples utility and common.ToAuthzExtTupleKeys
|
||||
func convertRolePermissionsToTuples(roleUID string, permissions []iamv0.CoreRolespecPermission) ([]*v1.TupleKey, error) {
|
||||
@@ -44,8 +55,8 @@ func convertRolePermissionsToTuples(roleUID string, permissions []iamv0.CoreRole
|
||||
|
||||
// AfterRoleCreate is a post-create hook that writes the role permissions to Zanzana (openFGA)
|
||||
// It handles both Role and CoreRole types
|
||||
func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object, _ *metav1.CreateOptions) {
|
||||
if b.zClient == nil {
|
||||
func (h *RoleHooks) AfterRoleCreate(obj runtime.Object, _ *metav1.CreateOptions) {
|
||||
if h.zClient == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -76,21 +87,21 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
|
||||
}
|
||||
|
||||
wait := time.Now()
|
||||
b.zTickets <- true
|
||||
h.zTickets <- true
|
||||
hooksWaitHistogram.WithLabelValues(rType, "create").Observe(time.Since(wait).Seconds())
|
||||
|
||||
go func(role *iamv0.CoreRole, roleType string) {
|
||||
start := time.Now()
|
||||
status := "success"
|
||||
defer func() {
|
||||
<-b.zTickets
|
||||
<-h.zTickets
|
||||
hooksDurationHistogram.WithLabelValues(rType, "create", status).Observe(time.Since(start).Seconds())
|
||||
hooksOperationCounter.WithLabelValues(rType, "create", status).Inc()
|
||||
}()
|
||||
|
||||
tuples, err := convertRolePermissionsToTuples(role.Name, role.Spec.Permissions)
|
||||
if err != nil {
|
||||
b.logger.Error("failed to convert role permissions to tuples",
|
||||
h.logger.Error("failed to convert role permissions to tuples",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -103,7 +114,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
|
||||
|
||||
// Avoid writing if there are no valid tuples
|
||||
if len(tuples) == 0 {
|
||||
b.logger.Debug("no valid tuples to write for role",
|
||||
h.logger.Debug("no valid tuples to write for role",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -113,7 +124,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
|
||||
return
|
||||
}
|
||||
|
||||
b.logger.Debug("writing role permissions to zanzana",
|
||||
h.logger.Debug("writing role permissions to zanzana",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -124,14 +135,14 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = b.zClient.Write(ctx, &v1.WriteRequest{
|
||||
err = h.zClient.Write(ctx, &v1.WriteRequest{
|
||||
Namespace: role.Namespace,
|
||||
Writes: &v1.WriteRequestWrites{
|
||||
TupleKeys: tuples,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
b.logger.Error("failed to write role permissions to zanzana",
|
||||
h.logger.Error("failed to write role permissions to zanzana",
|
||||
"err", err,
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
@@ -149,8 +160,8 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleCreate(obj runtime.Object,
|
||||
|
||||
// AfterRoleDelete is a post-delete hook that removes the role permissions from Zanzana (openFGA)
|
||||
// It handles both Role and CoreRole types
|
||||
func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object, _ *metav1.DeleteOptions) {
|
||||
if b.zClient == nil {
|
||||
func (h *RoleHooks) AfterRoleDelete(obj runtime.Object, _ *metav1.DeleteOptions) {
|
||||
if h.zClient == nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -182,15 +193,15 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
|
||||
}
|
||||
|
||||
wait := time.Now()
|
||||
b.zTickets <- true
|
||||
h.zTickets <- true
|
||||
hooksWaitHistogram.WithLabelValues("role", "delete").Observe(time.Since(wait).Seconds()) // Record wait time
|
||||
|
||||
go func(role *iamv0.CoreRole, roleType string) {
|
||||
defer func() {
|
||||
<-b.zTickets
|
||||
<-h.zTickets
|
||||
}()
|
||||
|
||||
b.logger.Debug("deleting role permissions from zanzana",
|
||||
h.logger.Debug("deleting role permissions from zanzana",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -199,7 +210,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
|
||||
|
||||
tuples, err := convertRolePermissionsToTuples(role.Name, role.Spec.Permissions)
|
||||
if err != nil {
|
||||
b.logger.Error("failed to convert role permissions to tuples for deletion",
|
||||
h.logger.Error("failed to convert role permissions to tuples for deletion",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -211,7 +222,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
|
||||
|
||||
// Avoid deleting if there are no valid tuples
|
||||
if len(tuples) == 0 {
|
||||
b.logger.Debug("no valid tuples to delete for role",
|
||||
h.logger.Debug("no valid tuples to delete for role",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -223,7 +234,7 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
|
||||
// Convert tuples to TupleKeyWithoutCondition for deletion
|
||||
deleteTuples := toTupleKeysWithoutCondition(tuples)
|
||||
|
||||
b.logger.Debug("deleting role permissions from zanzana",
|
||||
h.logger.Debug("deleting role permissions from zanzana",
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
"roleType", roleType,
|
||||
@@ -234,14 +245,14 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultWriteTimeout)
|
||||
defer cancel()
|
||||
|
||||
err = b.zClient.Write(ctx, &v1.WriteRequest{
|
||||
err = h.zClient.Write(ctx, &v1.WriteRequest{
|
||||
Namespace: role.Namespace,
|
||||
Deletes: &v1.WriteRequestDeletes{
|
||||
TupleKeys: deleteTuples,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
b.logger.Error("failed to delete role permissions from zanzana",
|
||||
h.logger.Error("failed to delete role permissions from zanzana",
|
||||
"err", err,
|
||||
"namespace", role.Namespace,
|
||||
"roleUID", role.Name,
|
||||
@@ -255,8 +266,8 @@ func (b *IdentityAccessManagementAPIBuilder) AfterRoleDelete(obj runtime.Object,
|
||||
// beginRoleUpdate is a pre-update hook that prepares zanzana updates
|
||||
// It converts old and new permissions to tuples and performs the zanzana write after K8s update succeeds
|
||||
// It handles both Role and CoreRole types
|
||||
func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context, obj, oldObj runtime.Object, options *metav1.UpdateOptions) (registry.FinishFunc, error) {
|
||||
if b.zClient == nil {
|
||||
func (h *RoleHooks) BeginRoleUpdate(ctx context.Context, obj, oldObj runtime.Object, options *metav1.UpdateOptions) (registry.FinishFunc, error) {
|
||||
if h.zClient == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var oldRole, newRole *iamv0.CoreRole
|
||||
@@ -316,12 +327,12 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
|
||||
// Grab a ticket to write to Zanzana
|
||||
wait := time.Now()
|
||||
b.zTickets <- true
|
||||
h.zTickets <- true
|
||||
hooksWaitHistogram.WithLabelValues(roleType, "update").Observe(time.Since(wait).Seconds()) // Record wait time
|
||||
|
||||
go func(old *iamv0.CoreRole, new *iamv0.CoreRole) {
|
||||
defer func() {
|
||||
<-b.zTickets
|
||||
<-h.zTickets
|
||||
}()
|
||||
roleUID, namespace := old.Name, old.Namespace
|
||||
oldPermissions, newPermissions := old.Spec.Permissions, new.Spec.Permissions
|
||||
@@ -332,7 +343,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
var err error
|
||||
oldTuples, err = convertRolePermissionsToTuples(roleUID, oldPermissions)
|
||||
if err != nil {
|
||||
b.logger.Error("failed to convert old role permissions to tuples",
|
||||
h.logger.Error("failed to convert old role permissions to tuples",
|
||||
"namespace", namespace,
|
||||
"roleUID", roleUID,
|
||||
"roleType", roleType,
|
||||
@@ -344,7 +355,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
// Convert new permissions to tuples for writing
|
||||
newTuples, err := convertRolePermissionsToTuples(roleUID, newPermissions)
|
||||
if err != nil {
|
||||
b.logger.Error("failed to convert new role permissions to tuples",
|
||||
h.logger.Error("failed to convert new role permissions to tuples",
|
||||
"namespace", namespace,
|
||||
"roleUID", roleUID,
|
||||
"roleType", roleType,
|
||||
@@ -353,7 +364,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
return
|
||||
}
|
||||
|
||||
b.logger.Debug("updating role permissions in zanzana",
|
||||
h.logger.Debug("updating role permissions in zanzana",
|
||||
"namespace", namespace,
|
||||
"roleUID", roleUID,
|
||||
"roleType", roleType,
|
||||
@@ -375,7 +386,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
req.Deletes = &v1.WriteRequestDeletes{
|
||||
TupleKeys: deleteTuples,
|
||||
}
|
||||
b.logger.Debug("deleting existing role permissions from zanzana",
|
||||
h.logger.Debug("deleting existing role permissions from zanzana",
|
||||
"namespace", namespace,
|
||||
"roleUID", roleUID,
|
||||
"roleType", roleType,
|
||||
@@ -388,7 +399,7 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
req.Writes = &v1.WriteRequestWrites{
|
||||
TupleKeys: newTuples,
|
||||
}
|
||||
b.logger.Debug("writing new role permissions to zanzana",
|
||||
h.logger.Debug("writing new role permissions to zanzana",
|
||||
"namespace", namespace,
|
||||
"roleUID", roleUID,
|
||||
"roleType", roleType,
|
||||
@@ -398,9 +409,9 @@ func (b *IdentityAccessManagementAPIBuilder) BeginRoleUpdate(ctx context.Context
|
||||
|
||||
// Only make the request if there are deletes or writes
|
||||
if req.Deletes != nil || req.Writes != nil {
|
||||
err = b.zClient.Write(ctx, req)
|
||||
err = h.zClient.Write(ctx, req)
|
||||
if err != nil {
|
||||
b.logger.Error("failed to update role permissions in zanzana",
|
||||
h.logger.Error("failed to update role permissions in zanzana",
|
||||
"err", err,
|
||||
"namespace", namespace,
|
||||
"roleUID", roleUID,
|
||||
|
||||
@@ -53,7 +53,7 @@ func requireDeleteTuplesMatch(t *testing.T, actual []*v1.TupleKeyWithoutConditio
|
||||
func TestAfterCoreRoleCreate(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
b := &IdentityAccessManagementAPIBuilder{
|
||||
h := &RoleHooks{
|
||||
logger: log.NewNopLogger(),
|
||||
zTickets: make(chan bool, 1),
|
||||
}
|
||||
@@ -90,8 +90,8 @@ func TestAfterCoreRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleEntries}
|
||||
b.AfterRoleCreate(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleEntries}
|
||||
h.AfterRoleCreate(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -129,8 +129,8 @@ func TestAfterCoreRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleEntries}
|
||||
b.AfterRoleCreate(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleEntries}
|
||||
h.AfterRoleCreate(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -163,8 +163,8 @@ func TestAfterCoreRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testWildcardEntries}
|
||||
b.AfterRoleCreate(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testWildcardEntries}
|
||||
h.AfterRoleCreate(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -198,15 +198,15 @@ func TestAfterCoreRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testMixedEntries}
|
||||
b.AfterRoleCreate(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testMixedEntries}
|
||||
h.AfterRoleCreate(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestAfterRoleCreate(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
b := &IdentityAccessManagementAPIBuilder{
|
||||
h := &RoleHooks{
|
||||
logger: log.NewNopLogger(),
|
||||
zTickets: make(chan bool, 1),
|
||||
}
|
||||
@@ -243,8 +243,8 @@ func TestAfterRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testRoleEntries}
|
||||
b.AfterRoleCreate(&role, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testRoleEntries}
|
||||
h.AfterRoleCreate(&role, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -281,8 +281,8 @@ func TestAfterRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testDashRoleEntries}
|
||||
b.AfterRoleCreate(&role, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testDashRoleEntries}
|
||||
h.AfterRoleCreate(&role, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -317,15 +317,15 @@ func TestAfterRoleCreate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testMergedEntries}
|
||||
b.AfterRoleCreate(&role, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testMergedEntries}
|
||||
h.AfterRoleCreate(&role, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestBeginCoreRoleUpdate(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
b := &IdentityAccessManagementAPIBuilder{
|
||||
h := &RoleHooks{
|
||||
logger: log.NewNopLogger(),
|
||||
zTickets: make(chan bool, 1),
|
||||
}
|
||||
@@ -387,10 +387,10 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
|
||||
|
||||
// Call BeginUpdate which does all the work
|
||||
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, finishFunc)
|
||||
|
||||
@@ -445,10 +445,10 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testExpand}
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testExpand}
|
||||
|
||||
// Call BeginUpdate which does all the work
|
||||
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, finishFunc)
|
||||
|
||||
@@ -499,10 +499,10 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testClear}
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testClear}
|
||||
|
||||
// Call BeginUpdate which does all the work
|
||||
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, finishFunc)
|
||||
|
||||
@@ -514,7 +514,7 @@ func TestBeginCoreRoleUpdate(t *testing.T) {
|
||||
|
||||
func TestBeginRoleUpdate(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
b := &IdentityAccessManagementAPIBuilder{
|
||||
h := &RoleHooks{
|
||||
logger: log.NewNopLogger(),
|
||||
zTickets: make(chan bool, 1),
|
||||
}
|
||||
@@ -577,10 +577,10 @@ func TestBeginRoleUpdate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testUpdate}
|
||||
|
||||
// Call BeginUpdate which does all the work
|
||||
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, finishFunc)
|
||||
|
||||
@@ -640,10 +640,10 @@ func TestBeginRoleUpdate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testSwap}
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testSwap}
|
||||
|
||||
// Call BeginUpdate which does all the work
|
||||
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, finishFunc)
|
||||
|
||||
@@ -695,10 +695,10 @@ func TestBeginRoleUpdate(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testAddToEmpty}
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testAddToEmpty}
|
||||
|
||||
// Call BeginUpdate which does all the work
|
||||
finishFunc, err := b.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
finishFunc, err := h.BeginRoleUpdate(context.Background(), &newRole, &oldRole, nil)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, finishFunc)
|
||||
|
||||
@@ -710,7 +710,7 @@ func TestBeginRoleUpdate(t *testing.T) {
|
||||
|
||||
func TestAfterCoreRoleDelete(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
b := &IdentityAccessManagementAPIBuilder{
|
||||
h := &RoleHooks{
|
||||
logger: log.NewNopLogger(),
|
||||
zTickets: make(chan bool, 1),
|
||||
}
|
||||
@@ -747,8 +747,8 @@ func TestAfterCoreRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleDeletes}
|
||||
b.AfterRoleDelete(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testCoreRoleDeletes}
|
||||
h.AfterRoleDelete(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -785,8 +785,8 @@ func TestAfterCoreRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleDeletes}
|
||||
b.AfterRoleDelete(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testDashboardRoleDeletes}
|
||||
h.AfterRoleDelete(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -818,8 +818,8 @@ func TestAfterCoreRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testWildcardDeletes}
|
||||
b.AfterRoleDelete(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testWildcardDeletes}
|
||||
h.AfterRoleDelete(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -853,15 +853,15 @@ func TestAfterCoreRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testMixedDeletes}
|
||||
b.AfterRoleDelete(&coreRole, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testMixedDeletes}
|
||||
h.AfterRoleDelete(&coreRole, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestAfterRoleDelete(t *testing.T) {
|
||||
var wg sync.WaitGroup
|
||||
b := &IdentityAccessManagementAPIBuilder{
|
||||
h := &RoleHooks{
|
||||
logger: log.NewNopLogger(),
|
||||
zTickets: make(chan bool, 1),
|
||||
}
|
||||
@@ -898,8 +898,8 @@ func TestAfterRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testRoleDeletes}
|
||||
b.AfterRoleDelete(&role, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testRoleDeletes}
|
||||
h.AfterRoleDelete(&role, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -936,8 +936,8 @@ func TestAfterRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testDashRoleDeletes}
|
||||
b.AfterRoleDelete(&role, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testDashRoleDeletes}
|
||||
h.AfterRoleDelete(&role, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
|
||||
@@ -982,8 +982,8 @@ func TestAfterRoleDelete(t *testing.T) {
|
||||
return nil
|
||||
}
|
||||
|
||||
b.zClient = &FakeZanzanaClient{writeCallback: testMultiDeletes}
|
||||
b.AfterRoleDelete(&role, nil)
|
||||
h.zClient = &FakeZanzanaClient{writeCallback: testMultiDeletes}
|
||||
h.AfterRoleDelete(&role, nil)
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
var WireSetExts = wire.NewSet(
|
||||
noopstorage.ProvideStorageBackend,
|
||||
wire.Bind(new(iam.CoreRoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.RoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
iam.ProvideNoopRoleApiInstaller,
|
||||
wire.Bind(new(iam.RoleBindingStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.ExternalGroupMappingStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
|
||||
|
||||
Generated
+4
-2
@@ -882,8 +882,9 @@ 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()
|
||||
roleApiInstaller := iam.ProvideNoopRoleApiInstaller()
|
||||
noopTeamGroupsREST := externalgroupmapping.ProvideNoopTeamGroupsREST()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, roleApiInstaller, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1543,8 +1544,9 @@ 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()
|
||||
roleApiInstaller := iam.ProvideNoopRoleApiInstaller()
|
||||
noopTeamGroupsREST := externalgroupmapping.ProvideNoopTeamGroupsREST()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(cfg, featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, roleApiInstaller, tracingService, storageBackendImpl, storageBackendImpl, noopTeamGroupsREST, dualwriteService, resourceClient, orgService, userService, teamService, eventualRestConfigProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user