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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user