Access Control: refactor permission evaluator to be more flexible (#35996)
* add a more flexible way to create permissions * update interface for accesscontrol to use new eval interface * use new eval interface * update middleware to use new eval interface * remove evaluator function and move metrics to service * add tests for accesscontrol middleware * Remove failed function from interface and update inejct to create a new evaluator * Change name * Support Several sopes for a permission * use evaluator and update fakeAccessControl * Implement String that will return string representation of permissions for an evaluator Co-authored-by: Gabriel MABILLE <gamab@users.noreply.github.com> Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
This commit is contained in:
co-authored by
Gabriel MABILLE
Emil Tullstedt
parent
9d8f61c738
commit
7ebf4027a7
@@ -1,68 +1,50 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
macaron "gopkg.in/macaron.v1"
|
||||
"gopkg.in/macaron.v1"
|
||||
|
||||
"github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
func Middleware(ac accesscontrol.AccessControl) func(macaron.Handler, string, ...string) macaron.Handler {
|
||||
return func(fallback macaron.Handler, permission string, scopes ...string) macaron.Handler {
|
||||
func Middleware(ac accesscontrol.AccessControl) func(macaron.Handler, accesscontrol.Evaluator) macaron.Handler {
|
||||
return func(fallback macaron.Handler, evaluator accesscontrol.Evaluator) macaron.Handler {
|
||||
if ac.IsDisabled() {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return func(c *models.ReqContext) {
|
||||
// We need this otherwise templated scopes get initialized only once, during the first call
|
||||
runtimeScope := make([]string, len(scopes))
|
||||
for i, scope := range scopes {
|
||||
var buf bytes.Buffer
|
||||
|
||||
tmpl, err := template.New("scope").Parse(scope)
|
||||
if err != nil {
|
||||
c.JsonApiErr(http.StatusInternalServerError, "Internal server error", err)
|
||||
return
|
||||
}
|
||||
err = tmpl.Execute(&buf, c.AllParams())
|
||||
if err != nil {
|
||||
c.JsonApiErr(http.StatusInternalServerError, "Internal server error", err)
|
||||
return
|
||||
}
|
||||
runtimeScope[i] = buf.String()
|
||||
}
|
||||
|
||||
hasAccess, err := ac.Evaluate(c.Req.Context(), c.SignedInUser, permission, runtimeScope...)
|
||||
injected, err := evaluator.Inject(c.AllParams())
|
||||
if err != nil {
|
||||
Deny(c, permission, runtimeScope, err)
|
||||
c.JsonApiErr(http.StatusInternalServerError, "Internal server error", err)
|
||||
return
|
||||
}
|
||||
if !hasAccess {
|
||||
Deny(c, permission, runtimeScope, nil)
|
||||
|
||||
hasAccess, err := ac.Evaluate(c.Req.Context(), c.SignedInUser, injected)
|
||||
if !hasAccess || err != nil {
|
||||
Deny(c, injected, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func Deny(c *models.ReqContext, permission string, scopes []string, err error) {
|
||||
func Deny(c *models.ReqContext, evaluator accesscontrol.Evaluator, err error) {
|
||||
id := newID()
|
||||
if err != nil {
|
||||
c.Logger.Error("Error from access control system", "error", err, "accessErrorID", id)
|
||||
} else {
|
||||
c.Logger.Info("Access denied",
|
||||
c.Logger.Info(
|
||||
"Access denied",
|
||||
"userID", c.UserId,
|
||||
"permission", permission,
|
||||
"scopes", scopes,
|
||||
"accessErrorID", id)
|
||||
"accessErrorID", id,
|
||||
"permissions", evaluator.String(),
|
||||
)
|
||||
}
|
||||
|
||||
// If the user triggers an error in the access control system, we
|
||||
|
||||
Reference in New Issue
Block a user