Folders: Use access checker for /access endpoint (#110745)
This commit is contained in:
@@ -55,6 +55,7 @@ type FolderAPIBuilder struct {
|
||||
ac accesscontrol.AccessControl
|
||||
storage grafanarest.Storage
|
||||
permissionStore reconcilers.PermissionStore
|
||||
accessClient authlib.AccessClient
|
||||
|
||||
authorizer authorizer.Authorizer
|
||||
parents parentsGetter
|
||||
@@ -83,6 +84,7 @@ func RegisterAPIService(cfg *setting.Cfg,
|
||||
folderPermissionsSvc: folderPermissionsSvc,
|
||||
acService: acService,
|
||||
ac: accessControl,
|
||||
accessClient: accessClient,
|
||||
permissionsOnCreate: cfg.RBAC.PermissionsOnCreation("folder"),
|
||||
authorizer: newLegacyAuthorizer(accessControl),
|
||||
searcher: unified,
|
||||
@@ -206,8 +208,8 @@ func (b *FolderAPIBuilder) UpdateAPIGroupInfo(apiGroupInfo *genericapiserver.API
|
||||
searcher: b.searcher,
|
||||
}
|
||||
storage[resourceInfo.StoragePath("access")] = &subAccessREST{
|
||||
getter: folderStore,
|
||||
ac: b.ac,
|
||||
getter: folderStore,
|
||||
accessClient: b.accessClient,
|
||||
}
|
||||
|
||||
// Adds a path to return children of a given folder
|
||||
|
||||
@@ -8,22 +8,23 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
|
||||
folders "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
authlib "github.com/grafana/authlib/types"
|
||||
foldersV1 "github.com/grafana/grafana/apps/folder/pkg/apis/folder/v1beta1"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/services/dashboards"
|
||||
"github.com/grafana/grafana/pkg/apimachinery/utils"
|
||||
"github.com/grafana/grafana/pkg/services/apiserver/endpoints/request"
|
||||
)
|
||||
|
||||
type subAccessREST struct {
|
||||
getter rest.Getter
|
||||
ac accesscontrol.AccessControl
|
||||
getter rest.Getter
|
||||
accessClient authlib.AccessClient
|
||||
}
|
||||
|
||||
var _ = rest.Connecter(&subAccessREST{})
|
||||
var _ = rest.StorageMetadata(&subAccessREST{})
|
||||
|
||||
func (r *subAccessREST) New() runtime.Object {
|
||||
return &folders.FolderAccessInfo{}
|
||||
return &foldersV1.FolderAccessInfo{}
|
||||
}
|
||||
|
||||
func (r *subAccessREST) Destroy() {
|
||||
@@ -38,7 +39,7 @@ func (r *subAccessREST) ProducesMIMETypes(verb string) []string {
|
||||
}
|
||||
|
||||
func (r *subAccessREST) ProducesObject(verb string) interface{} {
|
||||
return &folders.FolderAccessInfo{}
|
||||
return &foldersV1.FolderAccessInfo{}
|
||||
}
|
||||
|
||||
func (r *subAccessREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
@@ -46,28 +47,67 @@ func (r *subAccessREST) NewConnectOptions() (runtime.Object, bool, string) {
|
||||
}
|
||||
|
||||
func (r *subAccessREST) Connect(ctx context.Context, name string, opts runtime.Object, responder rest.Responder) (http.Handler, error) {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
access, err := r.getAccessInfo(ctx, name)
|
||||
if err != nil {
|
||||
responder.Error(err)
|
||||
} else {
|
||||
responder.Object(200, access)
|
||||
}
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (r *subAccessREST) getAccessInfo(ctx context.Context, name string) (*foldersV1.FolderAccessInfo, error) {
|
||||
ns, err := request.NamespaceInfoFrom(ctx, true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := identity.GetRequester(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Must be able to get the resource
|
||||
if _, err = r.getter.Get(ctx, name, &v1.GetOptions{}); err != nil {
|
||||
// Can view is managed here (and in the Authorizer)
|
||||
f, err := r.getter.Get(ctx, name, &v1.GetOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
obj, err := utils.MetaAccessor(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var tmp authlib.CheckResponse
|
||||
check := func(verb string) bool {
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
tmp, err = r.accessClient.Check(ctx, user, authlib.CheckRequest{
|
||||
Verb: verb,
|
||||
Group: foldersV1.GROUP,
|
||||
Resource: foldersV1.RESOURCE,
|
||||
Namespace: ns.Value,
|
||||
Name: name,
|
||||
Folder: obj.GetFolder(),
|
||||
})
|
||||
return tmp.Allowed
|
||||
}
|
||||
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
|
||||
access := &folders.FolderAccessInfo{}
|
||||
canEditEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(name))
|
||||
access.CanEdit, _ = r.ac.Evaluate(ctx, user, canEditEvaluator)
|
||||
access.CanSave = access.CanEdit
|
||||
canAdminEvaluator := accesscontrol.EvalAll(
|
||||
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsRead, dashboards.ScopeFoldersProvider.GetResourceScopeUID(name)),
|
||||
accesscontrol.EvalPermission(dashboards.ActionFoldersPermissionsWrite, dashboards.ScopeFoldersProvider.GetResourceScopeUID(name)),
|
||||
)
|
||||
access.CanAdmin, _ = r.ac.Evaluate(ctx, user, canAdminEvaluator)
|
||||
canDeleteEvaluator := accesscontrol.EvalPermission(dashboards.ActionFoldersDelete, dashboards.ScopeFoldersProvider.GetResourceScopeUID(name))
|
||||
access.CanDelete, _ = r.ac.Evaluate(ctx, user, canDeleteEvaluator)
|
||||
responder.Object(http.StatusOK, access)
|
||||
}), nil
|
||||
rsp := &foldersV1.FolderAccessInfo{}
|
||||
rsp.CanAdmin = check(utils.VerbSetPermissions)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp.CanDelete = rsp.CanAdmin || check(utils.VerbDelete)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp.CanEdit = rsp.CanAdmin || check(utils.VerbUpdate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rsp.CanSave = rsp.CanAdmin || check(utils.VerbCreate) // or the same as update?
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rsp, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user