RBAC: Add option to skip rbac check for specified verbs (#93654)

* Add option to skip rbac check for specified verbs
This commit is contained in:
Karl Persson
2024-09-24 15:13:04 +02:00
committed by GitHub
parent b04799dab9
commit c28b37a67b
13 changed files with 149 additions and 109 deletions
+27 -16
View File
@@ -7,29 +7,40 @@ import (
"github.com/grafana/authlib/claims"
"k8s.io/apiserver/pkg/authorization/authorizer"
iamv0 "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, claims.AccessClient) {
client := accesscontrol.NewLegacyAccessClient(ac, accesscontrol.ResourceAuthorizerOptions{
Resource: "users",
Attr: "id",
Mapping: map[string]string{
"get": accesscontrol.ActionOrgUsersRead,
"list": accesscontrol.ActionOrgUsersRead,
client := accesscontrol.NewLegacyAccessClient(
ac,
accesscontrol.ResourceAuthorizerOptions{
Resource: iamv0.UserResourceInfo.GetName(),
Attr: "id",
Mapping: map[string]string{
"get": accesscontrol.ActionOrgUsersRead,
"list": accesscontrol.ActionOrgUsersRead,
},
Resolver: accesscontrol.ResourceResolverFunc(func(ctx context.Context, ns claims.NamespaceInfo, name string) ([]string, error) {
res, err := store.GetUserInternalID(ctx, ns, legacy.GetUserInternalIDQuery{
UID: name,
})
if err != nil {
return nil, err
}
return []string{fmt.Sprintf("users:id:%d", res.ID)}, nil
}),
},
Resolver: accesscontrol.ResourceResolverFunc(func(ctx context.Context, ns claims.NamespaceInfo, name string) ([]string, error) {
res, err := store.GetUserInternalID(ctx, ns, legacy.GetUserInternalIDQuery{
UID: name,
})
if err != nil {
return nil, err
}
return []string{fmt.Sprintf("users:id:%d", res.ID)}, nil
}),
})
accesscontrol.ResourceAuthorizerOptions{
Resource: "display",
Unchecked: map[string]bool{
"get": true,
"list": true,
},
},
)
return gfauthorizer.NewResourceAuthorizer(client), client
}
+3 -10
View File
@@ -24,7 +24,6 @@ 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"
)
@@ -42,17 +41,11 @@ type IdentityAccessManagementAPIBuilder struct {
}
func RegisterAPIService(
features featuremgmt.FeatureToggles,
apiregistration builder.APIRegistrar,
ssoService ssosettings.Service,
sql db.DB,
ac accesscontrol.AccessControl,
) (*IdentityAccessManagementAPIBuilder, error) {
if !features.IsEnabledGlobally(featuremgmt.FlagGrafanaAPIServerWithExperimentalAPIs) {
// skip registration unless opting into experimental apis
return nil, nil
}
store := legacy.NewLegacySQLStores(legacysql.NewDatabaseProvider(sql))
authorizer, client := newLegacyAuthorizer(ac, store)
@@ -114,9 +107,9 @@ func (b *IdentityAccessManagementAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *ge
storage[userResource.StoragePath()] = user.NewLegacyStore(b.store, b.accessClient)
storage[userResource.StoragePath("teams")] = user.NewLegacyTeamMemberREST(b.store)
serviceaccountResource := iamv0.ServiceAccountResourceInfo
storage[serviceaccountResource.StoragePath()] = serviceaccount.NewLegacyStore(b.store)
storage[serviceaccountResource.StoragePath("tokens")] = serviceaccount.NewLegacyTokenREST(b.store)
serviceAccountResource := iamv0.ServiceAccountResourceInfo
storage[serviceAccountResource.StoragePath()] = serviceaccount.NewLegacyStore(b.store)
storage[serviceAccountResource.StoragePath("tokens")] = serviceaccount.NewLegacyTokenREST(b.store)
if b.sso != nil {
ssoResource := iamv0.SSOSettingResourceInfo
+1 -1
View File
@@ -97,7 +97,7 @@ var cfg = &setting.Cfg{}
func mapToTeamMember(m legacy.TeamMember) iamv0.TeamMember {
return iamv0.TeamMember{
IdentityDisplay: iamv0.IdentityDisplay{
Display: iamv0.Display{
Identity: iamv0.IdentityRef{
Type: claims.TypeUser,
Name: m.UserUID,
+14 -15
View File
@@ -35,7 +35,7 @@ func NewLegacyDisplayREST(store legacy.LegacyIdentityStore) *LegacyDisplayREST {
}
func (r *LegacyDisplayREST) New() runtime.Object {
return &iamv0.IdentityDisplayResults{}
return &iamv0.DisplayList{}
}
func (r *LegacyDisplayREST) Destroy() {}
@@ -45,8 +45,7 @@ func (r *LegacyDisplayREST) NamespaceScoped() bool {
}
func (r *LegacyDisplayREST) GetSingularName() string {
// not actually used anywhere, but required by SingularNameProvider
return "identitydisplay"
return "display"
}
func (r *LegacyDisplayREST) ProducesMIMETypes(verb string) []string {
@@ -54,11 +53,11 @@ func (r *LegacyDisplayREST) ProducesMIMETypes(verb string) []string {
}
func (r *LegacyDisplayREST) ProducesObject(verb string) any {
return &iamv0.IdentityDisplayResults{}
return &iamv0.DisplayList{}
}
func (r *LegacyDisplayREST) ConnectMethods() []string {
return []string{"GET"}
return []string{http.MethodGet}
}
func (r *LegacyDisplayREST) NewConnectOptions() (runtime.Object, bool, string) {
@@ -91,13 +90,13 @@ func (r *LegacyDisplayREST) Connect(ctx context.Context, name string, _ runtime.
return
}
rsp := &iamv0.IdentityDisplayResults{
rsp := &iamv0.DisplayList{
Keys: keys.keys,
InvalidKeys: keys.invalid,
Display: make([]iamv0.IdentityDisplay, 0, len(users.Users)+len(keys.disp)+1),
Items: make([]iamv0.Display, 0, len(users.Users)+len(keys.disp)+1),
}
for _, user := range users.Users {
disp := iamv0.IdentityDisplay{
disp := iamv0.Display{
Identity: iamv0.IdentityRef{
Type: claims.TypeUser,
Name: user.UID,
@@ -109,12 +108,12 @@ func (r *LegacyDisplayREST) Connect(ctx context.Context, name string, _ runtime.
disp.Identity.Type = claims.TypeServiceAccount
}
disp.AvatarURL = dtos.GetGravatarUrlWithDefault(fakeCfgForGravatar, user.Email, disp.DisplayName)
rsp.Display = append(rsp.Display, disp)
rsp.Items = append(rsp.Items, disp)
}
// Append the constants here
if len(keys.disp) > 0 {
rsp.Display = append(rsp.Display, keys.disp...)
rsp.Items = append(rsp.Items, keys.disp...)
}
responder.Object(200, rsp)
}), nil
@@ -127,7 +126,7 @@ type dispKeys struct {
invalid []string
// For terminal keys, this is a constant
disp []iamv0.IdentityDisplay
disp []iamv0.Display
}
func parseKeys(req []string) dispKeys {
@@ -148,7 +147,7 @@ func parseKeys(req []string) dispKeys {
switch t {
case claims.TypeAnonymous:
keys.disp = append(keys.disp, iamv0.IdentityDisplay{
keys.disp = append(keys.disp, iamv0.Display{
Identity: iamv0.IdentityRef{
Type: t,
},
@@ -157,7 +156,7 @@ func parseKeys(req []string) dispKeys {
})
continue
case claims.TypeAPIKey:
keys.disp = append(keys.disp, iamv0.IdentityDisplay{
keys.disp = append(keys.disp, iamv0.Display{
Identity: iamv0.IdentityRef{
Type: t,
Name: key,
@@ -167,7 +166,7 @@ func parseKeys(req []string) dispKeys {
})
continue
case claims.TypeProvisioning:
keys.disp = append(keys.disp, iamv0.IdentityDisplay{
keys.disp = append(keys.disp, iamv0.Display{
Identity: iamv0.IdentityRef{
Type: t,
},
@@ -184,7 +183,7 @@ func parseKeys(req []string) dispKeys {
id, err := strconv.ParseInt(key, 10, 64)
if err == nil {
if id == 0 {
keys.disp = append(keys.disp, iamv0.IdentityDisplay{
keys.disp = append(keys.disp, iamv0.Display{
Identity: iamv0.IdentityRef{
Type: claims.TypeUser,
Name: key,