diff --git a/apps/iam/pkg/apis/iam/v0alpha1/register.go b/apps/iam/pkg/apis/iam/v0alpha1/register.go index c2a3fb14b1a..3e0d4561290 100644 --- a/apps/iam/pkg/apis/iam/v0alpha1/register.go +++ b/apps/iam/pkg/apis/iam/v0alpha1/register.go @@ -23,7 +23,8 @@ var CoreRoleInfo = utils.NewResourceInfo(GROUP, VERSION, 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: "Group", Type: "string", Format: "group", Description: "Core role group"}, + {Name: "Title", Type: "string", Format: "string", Description: "Core role name"}, {Name: "Created At", Type: "date"}, }, Reader: func(obj any) ([]interface{}, error) { @@ -32,6 +33,7 @@ var CoreRoleInfo = utils.NewResourceInfo(GROUP, VERSION, if core != nil { return []interface{}{ core.Name, + core.Spec.Group, core.Spec.Title, core.CreationTimestamp.UTC().Format(time.RFC3339), }, nil @@ -42,6 +44,34 @@ var CoreRoleInfo = utils.NewResourceInfo(GROUP, VERSION, }, ) +var RoleInfo = utils.NewResourceInfo(GROUP, VERSION, + "roles", "role", "Role", + func() runtime.Object { return &Role{} }, + func() runtime.Object { return &RoleList{} }, + utils.TableColumns{ + Definition: []metav1.TableColumnDefinition{ + {Name: "Name", Type: "string", Format: "name"}, + {Name: "Group", Type: "string", Format: "group", Description: "Role group"}, + {Name: "Title", Type: "string", Format: "string", Description: "Role name"}, + {Name: "Created At", Type: "date"}, + }, + Reader: func(obj any) ([]interface{}, error) { + role, ok := obj.(*Role) + if ok { + if role != nil { + return []interface{}{ + role.Name, + role.Spec.Group, + role.Spec.Title, + role.CreationTimestamp.UTC().Format(time.RFC3339), + }, nil + } + } + return nil, fmt.Errorf("expected role") + }, + }, +) + var ( SchemeBuilder runtime.SchemeBuilder localSchemeBuilder = &SchemeBuilder @@ -58,6 +88,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(schemeGroupVersion, &CoreRole{}, &CoreRoleList{}, + &Role{}, + &RoleList{}, // What is this about? &metav1.PartialObjectMetadata{}, diff --git a/pkg/registry/apis/iam/authorizer.go b/pkg/registry/apis/iam/authorizer.go index e7938a0b754..42402ed769f 100644 --- a/pkg/registry/apis/iam/authorizer.go +++ b/pkg/registry/apis/iam/authorizer.go @@ -32,6 +32,7 @@ func newIAMAuthorizer(accessClient authlib.AccessClient, legacyAccessClient auth // Access specific resources authorizer := gfauthorizer.NewResourceAuthorizer(accessClient) resourceAuthorizer[iamv0.CoreRoleInfo.GetName()] = authorizer + resourceAuthorizer[iamv0.RoleInfo.GetName()] = authorizer return &iamAuthorizer{resourceAuthorizer: resourceAuthorizer} } diff --git a/pkg/registry/apis/iam/models.go b/pkg/registry/apis/iam/models.go index d4c96edf03a..ac3c21643af 100644 --- a/pkg/registry/apis/iam/models.go +++ b/pkg/registry/apis/iam/models.go @@ -17,14 +17,19 @@ var _ builder.APIGroupValidation = (*IdentityAccessManagementAPIBuilder)(nil) var _ builder.APIGroupMutation = (*IdentityAccessManagementAPIBuilder)(nil) // CoreRoleStorageBackend uses the resource.StorageBackend interface to provide storage for core roles. -// Used wire to identify the storage backend for core roles. +// Used by wire to identify the storage backend for core roles. type CoreRoleStorageBackend interface{ resource.StorageBackend } +// RoleStorageBackend uses the resource.StorageBackend interface to provide storage for custom roles. +// Used by wire to identify the storage backend for custom roles. +type RoleStorageBackend 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 // Access Control authorizer authorizer.Authorizer diff --git a/pkg/registry/apis/iam/register.go b/pkg/registry/apis/iam/register.go index 22d15d2e518..f1de0924641 100644 --- a/pkg/registry/apis/iam/register.go +++ b/pkg/registry/apis/iam/register.go @@ -50,6 +50,7 @@ func RegisterAPIService( accessClient types.AccessClient, reg prometheus.Registerer, coreRolesStorage CoreRoleStorageBackend, + rolesStorage RoleStorageBackend, ) (*IdentityAccessManagementAPIBuilder, error) { store := legacy.NewLegacySQLStores(legacysql.NewDatabaseProvider(sql)) legacyAccessClient := newLegacyAccessClient(ac, store) @@ -58,6 +59,7 @@ func RegisterAPIService( builder := &IdentityAccessManagementAPIBuilder{ store: store, coreRolesStorage: coreRolesStorage, + rolesStorage: rolesStorage, sso: ssoService, authorizer: authorizer, legacyAccessClient: legacyAccessClient, @@ -157,11 +159,17 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge if b.enableAuthZApis { // v0alpha1 - store, err := NewLocalStore(iamv0.CoreRoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.coreRolesStorage) + coreRoleStore, err := NewLocalStore(iamv0.CoreRoleInfo, apiGroupInfo.Scheme, opts.OptsGetter, b.reg, b.accessClient, b.coreRolesStorage) if err != nil { return err } - storage[iamv0.CoreRoleInfo.StoragePath()] = store + 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 } apiGroupInfo.VersionedResourcesStorageMap[legacyiamv0.VERSION] = storage diff --git a/pkg/registry/apis/wireset.go b/pkg/registry/apis/wireset.go index ebea70d289b..006d81b421c 100644 --- a/pkg/registry/apis/wireset.go +++ b/pkg/registry/apis/wireset.go @@ -24,6 +24,7 @@ import ( var WireSetExts = wire.NewSet( noopstorage.ProvideStorageBackend, wire.Bind(new(iam.CoreRoleStorageBackend), new(*noopstorage.StorageBackendImpl)), + wire.Bind(new(iam.RoleStorageBackend), new(*noopstorage.StorageBackendImpl)), ) var WireSet = wire.NewSet( diff --git a/pkg/server/wire_gen.go b/pkg/server/wire_gen.go index 440ede3c8b9..481ad2b1845 100644 --- a/pkg/server/wire_gen.go +++ b/pkg/server/wire_gen.go @@ -735,7 +735,7 @@ func Initialize(cfg *setting.Cfg, opts Options, apiOpts api.ServerOptions) (*Ser } folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, registerer, resourceClient) storageBackendImpl := noopstorage.ProvideStorageBackend() - identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, registerer, storageBackendImpl) + identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, registerer, storageBackendImpl, storageBackendImpl) if err != nil { return nil, err } @@ -1293,7 +1293,7 @@ func InitializeForTest(t sqlutil.ITestDB, testingT interface { } folderAPIBuilder := folders.RegisterAPIService(cfg, featureToggles, apiserverService, folderimplService, folderPermissionsService, accessControl, acimplService, registerer, resourceClient) storageBackendImpl := noopstorage.ProvideStorageBackend() - identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, registerer, storageBackendImpl) + identityAccessManagementAPIBuilder, err := iam.RegisterAPIService(featureToggles, apiserverService, ssosettingsimplService, sqlStore, accessControl, accessClient, registerer, storageBackendImpl, storageBackendImpl) if err != nil { return nil, err } diff --git a/pkg/services/authz/rbac/mapper.go b/pkg/services/authz/rbac/mapper.go index 789d3dd31a5..ded19301c83 100644 --- a/pkg/services/authz/rbac/mapper.go +++ b/pkg/services/authz/rbac/mapper.go @@ -105,6 +105,7 @@ func NewMapperRegistry() MapperRegistry { // Teams is a special case. We translate user permissions from id to uid based. "teams": newResourceTranslation("teams", "uid", false), "coreroles": newResourceTranslation("roles", "uid", false), + "roles": newResourceTranslation("roles", "uid", false), }, "secret.grafana.app": { "securevalues": newResourceTranslation("secret.securevalues", "uid", false),