IAM: Add ExternalGroupMapping kind for TeamSync (#113052)
* wip
* wip
* Add authorizer -> VERIFY it's working correctly
* Update openapi definitions
* Authorizer wip
* regen apis
* Increase timeout of pg int tests to 20m
* Revert "Increase timeout of pg int tests to 20m"
This reverts commit 8c20568217.
* Fix NewTestStore when Truncate is enabled
This commit is contained in:
@@ -35,6 +35,7 @@ func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient auth
|
||||
resourceAuthorizer[iamv0.RoleBindingInfo.GetName()] = authorizer
|
||||
resourceAuthorizer[iamv0.ServiceAccountResourceInfo.GetName()] = authorizer
|
||||
resourceAuthorizer[iamv0.UserResourceInfo.GetName()] = authorizer
|
||||
resourceAuthorizer[iamv0.ExternalGroupMappingResourceInfo.GetName()] = authorizer
|
||||
resourceAuthorizer[iamv0.TeamResourceInfo.GetName()] = authorizer
|
||||
|
||||
return &iamAuthorizer{resourceAuthorizer: resourceAuthorizer}
|
||||
|
||||
@@ -35,14 +35,19 @@ type RoleStorageBackend interface{ resource.StorageBackend }
|
||||
// Used by wire to identify the storage backend for role bindings.
|
||||
type RoleBindingStorageBackend interface{ resource.StorageBackend }
|
||||
|
||||
// ExternalGroupMappingStorageBackend uses the resource.StorageBackend interface to provide storage for external group mappings.
|
||||
// Used by wire to identify the storage backend for external group mappings.
|
||||
type ExternalGroupMappingStorageBackend interface{ resource.StorageBackend }
|
||||
|
||||
// This is used just so wire has something unique to return
|
||||
type IdentityAccessManagementAPIBuilder struct {
|
||||
// Stores
|
||||
store legacy.LegacyIdentityStore
|
||||
coreRolesStorage CoreRoleStorageBackend
|
||||
rolesStorage RoleStorageBackend
|
||||
resourcePermissionsStorage resource.StorageBackend
|
||||
roleBindingsStorage RoleBindingStorageBackend
|
||||
store legacy.LegacyIdentityStore
|
||||
coreRolesStorage CoreRoleStorageBackend
|
||||
rolesStorage RoleStorageBackend
|
||||
resourcePermissionsStorage resource.StorageBackend
|
||||
roleBindingsStorage RoleBindingStorageBackend
|
||||
externalGroupMappingStorage ExternalGroupMappingStorageBackend
|
||||
|
||||
// Access Control
|
||||
authorizer authorizer.Authorizer
|
||||
|
||||
@@ -63,6 +63,7 @@ func RegisterAPIService(
|
||||
coreRolesStorage CoreRoleStorageBackend,
|
||||
rolesStorage RoleStorageBackend,
|
||||
roleBindingsStorage RoleBindingStorageBackend,
|
||||
externalGroupMappingStorageBackend ExternalGroupMappingStorageBackend,
|
||||
dual dualwrite.Service,
|
||||
unified resource.ResourceClient,
|
||||
userService legacyuser.Service,
|
||||
@@ -74,25 +75,26 @@ func RegisterAPIService(
|
||||
registerMetrics(reg)
|
||||
|
||||
builder := &IdentityAccessManagementAPIBuilder{
|
||||
store: store,
|
||||
coreRolesStorage: coreRolesStorage,
|
||||
rolesStorage: rolesStorage,
|
||||
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
|
||||
roleBindingsStorage: roleBindingsStorage,
|
||||
sso: ssoService,
|
||||
authorizer: authorizer,
|
||||
legacyAccessClient: legacyAccessClient,
|
||||
accessClient: accessClient,
|
||||
zClient: zClient,
|
||||
zTickets: make(chan bool, MaxConcurrentZanzanaWrites),
|
||||
display: user.NewLegacyDisplayREST(store),
|
||||
reg: reg,
|
||||
logger: log.New("iam.apis"),
|
||||
features: features,
|
||||
enableDualWriter: true,
|
||||
dual: dual,
|
||||
unified: unified,
|
||||
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(), unified, user.NewUserLegacySearchClient(userService), features),
|
||||
store: store,
|
||||
coreRolesStorage: coreRolesStorage,
|
||||
rolesStorage: rolesStorage,
|
||||
resourcePermissionsStorage: resourcepermission.ProvideStorageBackend(dbProvider),
|
||||
roleBindingsStorage: roleBindingsStorage,
|
||||
externalGroupMappingStorage: externalGroupMappingStorageBackend,
|
||||
sso: ssoService,
|
||||
authorizer: authorizer,
|
||||
legacyAccessClient: legacyAccessClient,
|
||||
accessClient: accessClient,
|
||||
zClient: zClient,
|
||||
zTickets: make(chan bool, MaxConcurrentZanzanaWrites),
|
||||
display: user.NewLegacyDisplayREST(store),
|
||||
reg: reg,
|
||||
logger: log.New("iam.apis"),
|
||||
features: features,
|
||||
enableDualWriter: true,
|
||||
dual: dual,
|
||||
unified: unified,
|
||||
userSearchClient: resource.NewSearchClient(dualwrite.NewSearchAdapter(dual), iamv0.UserResourceInfo.GroupResource(), unified, user.NewUserLegacySearchClient(userService), features),
|
||||
}
|
||||
apiregistration.RegisterAPI(builder)
|
||||
|
||||
@@ -289,6 +291,27 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
|
||||
storage[ssoResource.StoragePath()] = sso.NewLegacyStore(b.sso)
|
||||
}
|
||||
|
||||
externalGroupMappingResource := iamv0.ExternalGroupMappingResourceInfo
|
||||
externalGroupMappingLegacyStore, err := NewLocalStore(externalGroupMappingResource, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.externalGroupMappingStorage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
storage[externalGroupMappingResource.StoragePath()] = externalGroupMappingLegacyStore
|
||||
|
||||
if b.enableDualWriter {
|
||||
externalGroupMappingStore, err := grafanaregistry.NewRegistryStore(opts.Scheme, externalGroupMappingResource, opts.OptsGetter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
externalGroupMappingDW, err := opts.DualWriteBuilder(externalGroupMappingResource.GroupResource(), externalGroupMappingLegacyStore, externalGroupMappingStore)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
storage[externalGroupMappingResource.StoragePath()] = externalGroupMappingDW
|
||||
}
|
||||
|
||||
//nolint:staticcheck // not yet migrated to OpenFeature
|
||||
if b.features.IsEnabledGlobally(featuremgmt.FlagKubernetesAuthzApis) {
|
||||
// v0alpha1
|
||||
|
||||
@@ -28,6 +28,7 @@ var WireSetExts = wire.NewSet(
|
||||
wire.Bind(new(iam.CoreRoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.RoleStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.RoleBindingStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
wire.Bind(new(iam.ExternalGroupMappingStorageBackend), new(*noopstorage.StorageBackendImpl)),
|
||||
)
|
||||
|
||||
var provisioningExtras = wire.NewSet(
|
||||
|
||||
Generated
+2
-2
@@ -848,7 +848,7 @@ func Initialize(ctx context.Context, cfg *setting.Cfg, opts Options, apiOpts api
|
||||
}
|
||||
folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, accessClient, registerer, resourceClient, zanzanaClient)
|
||||
storageBackendImpl := noopstorage.ProvideStorageBackend()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, dualwriteService, resourceClient, userService)
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, storageBackendImpl, dualwriteService, resourceClient, userService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1482,7 +1482,7 @@ func InitializeForTest(ctx context.Context, t sqlutil.ITestDB, testingT interfac
|
||||
}
|
||||
folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, accessClient, registerer, resourceClient, zanzanaClient)
|
||||
storageBackendImpl := noopstorage.ProvideStorageBackend()
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, dualwriteService, resourceClient, userService)
|
||||
identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, zanzanaClient, registerer, storageBackendImpl, storageBackendImpl, storageBackendImpl, storageBackendImpl, dualwriteService, resourceClient, userService)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -182,6 +182,25 @@ func newFolderTranslation() translation {
|
||||
return folderTranslation
|
||||
}
|
||||
|
||||
func newExternalGroupMappingTranslation() translation {
|
||||
return translation{
|
||||
resource: "teams.permissions",
|
||||
attribute: "uid",
|
||||
verbMapping: map[string]string{
|
||||
utils.VerbGet: "teams.permissions:read",
|
||||
utils.VerbList: "teams.permissions:read",
|
||||
utils.VerbWatch: "teams.permissions:read",
|
||||
utils.VerbCreate: "teams.permissions:write",
|
||||
utils.VerbUpdate: "teams.permissions:write",
|
||||
utils.VerbPatch: "teams.permissions:write",
|
||||
utils.VerbDelete: "teams.permissions:write",
|
||||
utils.VerbGetPermissions: "teams.permissions:write",
|
||||
utils.VerbSetPermissions: "teams.permissions:write",
|
||||
},
|
||||
folderSupport: false,
|
||||
}
|
||||
}
|
||||
|
||||
func NewMapperRegistry() MapperRegistry {
|
||||
skipScopeOnAllVerbs := map[string]bool{
|
||||
utils.VerbCreate: true,
|
||||
@@ -210,6 +229,8 @@ func NewMapperRegistry() MapperRegistry {
|
||||
"serviceaccounts": newResourceTranslation("serviceaccounts", "uid", false, map[string]bool{utils.VerbCreate: true}),
|
||||
// Teams is a special case. We translate user permissions from id to uid based.
|
||||
"teams": newResourceTranslation("teams", "uid", false, map[string]bool{utils.VerbCreate: true}),
|
||||
// ExternalGroupMappings is a special case. We translate team permissions from id to uid based.
|
||||
"externalgroupmappings": newExternalGroupMappingTranslation(),
|
||||
"coreroles": translation{
|
||||
resource: "roles",
|
||||
attribute: "uid",
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/grafana/authlib/types"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/common"
|
||||
"github.com/grafana/grafana/pkg/registry/apis/iam/legacy"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
)
|
||||
@@ -54,7 +55,7 @@ func (s *Service) newServiceAccountNameResolver(ctx context.Context, ns types.Na
|
||||
func (s *Service) fetchTeams(ctx context.Context, ns types.NamespaceInfo) (map[int64]string, error) {
|
||||
key := teamIDsCacheKey(ns.Value)
|
||||
res, err, _ := s.sf.Do(key, func() (any, error) {
|
||||
teams, err := s.identityStore.ListTeams(ctx, ns, legacy.ListTeamQuery{})
|
||||
teams, err := s.identityStore.ListTeams(ctx, ns, legacy.ListTeamQuery{Pagination: common.Pagination{Limit: 100}})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not fetch teams: %w", err)
|
||||
}
|
||||
@@ -170,6 +171,7 @@ func (s *Service) nameResolver(ctx context.Context, ns types.NamespaceInfo, scop
|
||||
if scopePrefix == "teams:id:" {
|
||||
return s.newTeamNameResolver(ctx, ns)
|
||||
}
|
||||
|
||||
if scopePrefix == "permissions:type:" {
|
||||
return permissionsDelegateResolverFunc, nil
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ func NewTestStore(tb TestingTB, opts ...TestOption) *SQLStore {
|
||||
tb.Fatalf("failed to truncate DB tables after migrations: %v", err)
|
||||
panic("unreachable")
|
||||
}
|
||||
testSQLStore.engine.ResetSequenceGenerator()
|
||||
store.engine.ResetSequenceGenerator()
|
||||
}
|
||||
|
||||
return store
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user