mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AccessControl: Extend scope parameters with extra params from context (#39722)
* AccessControl: Extend scope parameters with extra params from context Co-authored-by: Emil Tullstedt <emil.tullstedt@grafana.com>
This commit is contained in:
@@ -17,17 +17,17 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// API related scopes
|
// API related scopes
|
||||||
const (
|
var (
|
||||||
ScopeProvisionersAll = "provisioners:*"
|
ScopeProvisionersAll = accesscontrol.Scope("provisioners", "*")
|
||||||
ScopeProvisionersDashboards = "provisioners:dashboards"
|
ScopeProvisionersDashboards = accesscontrol.Scope("provisioners", "dashboards")
|
||||||
ScopeProvisionersPlugins = "provisioners:plugins"
|
ScopeProvisionersPlugins = accesscontrol.Scope("provisioners", "plugins")
|
||||||
ScopeProvisionersDatasources = "provisioners:datasources"
|
ScopeProvisionersDatasources = accesscontrol.Scope("provisioners", "datasources")
|
||||||
ScopeProvisionersNotifications = "provisioners:notifications"
|
ScopeProvisionersNotifications = accesscontrol.Scope("provisioners", "notifications")
|
||||||
|
|
||||||
ScopeDatasourcesAll = `datasources:*`
|
ScopeDatasourcesAll = accesscontrol.Scope("datasources", "*")
|
||||||
ScopeDatasourceID = `datasources:id:{{ index . ":id" }}`
|
ScopeDatasourceID = accesscontrol.Scope("datasources", "id", accesscontrol.Parameter(":id"))
|
||||||
ScopeDatasourceUID = `datasources:uid:{{ index . ":uid" }}`
|
ScopeDatasourceUID = accesscontrol.Scope("datasources", "uid", accesscontrol.Parameter(":uid"))
|
||||||
ScopeDatasourceName = `datasources:name:{{ index . ":name" }}`
|
ScopeDatasourceName = accesscontrol.Scope("datasources", "name", accesscontrol.Parameter(":name"))
|
||||||
)
|
)
|
||||||
|
|
||||||
// declareFixedRoles declares to the AccessControl service fixed roles and their
|
// declareFixedRoles declares to the AccessControl service fixed roles and their
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ type Evaluator interface {
|
|||||||
// Evaluate permissions that are grouped by action
|
// Evaluate permissions that are grouped by action
|
||||||
Evaluate(permissions map[string]map[string]struct{}) (bool, error)
|
Evaluate(permissions map[string]map[string]struct{}) (bool, error)
|
||||||
// Inject params into the evaluator's templated scopes. e.g. "settings:" + eval.Parameters(":id") and returns a new Evaluator
|
// Inject params into the evaluator's templated scopes. e.g. "settings:" + eval.Parameters(":id") and returns a new Evaluator
|
||||||
Inject(params map[string]string) (Evaluator, error)
|
Inject(params ScopeParams) (Evaluator, error)
|
||||||
// String returns a string representation of permission required by the evaluator
|
// String returns a string representation of permission required by the evaluator
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
@@ -89,7 +89,7 @@ func match(scope, target string) (bool, error) {
|
|||||||
return scope == target, nil
|
return scope == target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p permissionEvaluator) Inject(params map[string]string) (Evaluator, error) {
|
func (p permissionEvaluator) Inject(params ScopeParams) (Evaluator, error) {
|
||||||
scopes := make([]string, 0, len(p.Scopes))
|
scopes := make([]string, 0, len(p.Scopes))
|
||||||
for _, scope := range p.Scopes {
|
for _, scope := range p.Scopes {
|
||||||
tmpl, err := template.New("scope").Parse(scope)
|
tmpl, err := template.New("scope").Parse(scope)
|
||||||
@@ -129,7 +129,7 @@ func (a allEvaluator) Evaluate(permissions map[string]map[string]struct{}) (bool
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a allEvaluator) Inject(params map[string]string) (Evaluator, error) {
|
func (a allEvaluator) Inject(params ScopeParams) (Evaluator, error) {
|
||||||
var injected []Evaluator
|
var injected []Evaluator
|
||||||
for _, e := range a.allOf {
|
for _, e := range a.allOf {
|
||||||
i, err := e.Inject(params)
|
i, err := e.Inject(params)
|
||||||
@@ -173,7 +173,7 @@ func (a anyEvaluator) Evaluate(permissions map[string]map[string]struct{}) (bool
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a anyEvaluator) Inject(params map[string]string) (Evaluator, error) {
|
func (a anyEvaluator) Inject(params ScopeParams) (Evaluator, error) {
|
||||||
var injected []Evaluator
|
var injected []Evaluator
|
||||||
for _, e := range a.anyOf {
|
for _, e := range a.anyOf {
|
||||||
i, err := e.Inject(params)
|
i, err := e.Inject(params)
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type injectTestCase struct {
|
|||||||
desc string
|
desc string
|
||||||
expected bool
|
expected bool
|
||||||
evaluator Evaluator
|
evaluator Evaluator
|
||||||
params map[string]string
|
params ScopeParams
|
||||||
permissions map[string]map[string]struct{}
|
permissions map[string]map[string]struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,13 +77,28 @@ func TestPermission_Evaluate(t *testing.T) {
|
|||||||
|
|
||||||
func TestPermission_Inject(t *testing.T) {
|
func TestPermission_Inject(t *testing.T) {
|
||||||
tests := []injectTestCase{
|
tests := []injectTestCase{
|
||||||
|
{
|
||||||
|
desc: "should inject field",
|
||||||
|
expected: true,
|
||||||
|
evaluator: EvalPermission("orgs:read", Scope("orgs", Field("OrgID"))),
|
||||||
|
params: ScopeParams{
|
||||||
|
OrgID: 3,
|
||||||
|
},
|
||||||
|
permissions: map[string]map[string]struct{}{
|
||||||
|
"orgs:read": {
|
||||||
|
"orgs:3": struct{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "should inject correct param",
|
desc: "should inject correct param",
|
||||||
expected: true,
|
expected: true,
|
||||||
evaluator: EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
evaluator: EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
||||||
params: map[string]string{
|
params: ScopeParams{
|
||||||
":id": "10",
|
URLParams: map[string]string{
|
||||||
":reportId": "1",
|
":id": "10",
|
||||||
|
":reportId": "1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
@@ -95,7 +110,7 @@ func TestPermission_Inject(t *testing.T) {
|
|||||||
desc: "should fail for nil params",
|
desc: "should fail for nil params",
|
||||||
expected: false,
|
expected: false,
|
||||||
evaluator: EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
evaluator: EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
||||||
params: nil,
|
params: ScopeParams{},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
"reports:1": struct{}{},
|
"reports:1": struct{}{},
|
||||||
@@ -106,9 +121,11 @@ func TestPermission_Inject(t *testing.T) {
|
|||||||
desc: "should inject several parameters to one permission",
|
desc: "should inject several parameters to one permission",
|
||||||
expected: true,
|
expected: true,
|
||||||
evaluator: EvalPermission("reports:read", Scope("reports", Parameter(":reportId"), Parameter(":reportId2"))),
|
evaluator: EvalPermission("reports:read", Scope("reports", Parameter(":reportId"), Parameter(":reportId2"))),
|
||||||
params: map[string]string{
|
params: ScopeParams{
|
||||||
":reportId": "report",
|
URLParams: map[string]string{
|
||||||
":reportId2": "report2",
|
":reportId": "report",
|
||||||
|
":reportId2": "report2",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
@@ -187,10 +204,12 @@ func TestAll_Inject(t *testing.T) {
|
|||||||
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
||||||
EvalPermission("settings:read", Scope("settings", Parameter(":settingsId"))),
|
EvalPermission("settings:read", Scope("settings", Parameter(":settingsId"))),
|
||||||
),
|
),
|
||||||
params: map[string]string{
|
params: ScopeParams{
|
||||||
":id": "10",
|
URLParams: map[string]string{
|
||||||
":settingsId": "3",
|
":id": "10",
|
||||||
":reportId": "1",
|
":settingsId": "3",
|
||||||
|
":reportId": "1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
@@ -201,6 +220,26 @@ func TestAll_Inject(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "should inject field and URL param",
|
||||||
|
expected: true,
|
||||||
|
evaluator: EvalAll(
|
||||||
|
EvalPermission("orgs:read", Scope("orgs", Field("OrgID"))),
|
||||||
|
EvalPermission("orgs:read", Scope("orgs", Parameter(":orgId"))),
|
||||||
|
),
|
||||||
|
params: ScopeParams{
|
||||||
|
OrgID: 3,
|
||||||
|
URLParams: map[string]string{
|
||||||
|
":orgId": "4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
permissions: map[string]map[string]struct{}{
|
||||||
|
"orgs:read": {
|
||||||
|
"orgs:3": struct{}{},
|
||||||
|
"orgs:4": struct{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "should fail for nil params",
|
desc: "should fail for nil params",
|
||||||
expected: false,
|
expected: false,
|
||||||
@@ -208,7 +247,7 @@ func TestAll_Inject(t *testing.T) {
|
|||||||
EvalPermission("settings:read", Scope("reports", Parameter(":settingsId"))),
|
EvalPermission("settings:read", Scope("reports", Parameter(":settingsId"))),
|
||||||
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
||||||
),
|
),
|
||||||
params: nil,
|
params: ScopeParams{},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
"reports:1": struct{}{},
|
"reports:1": struct{}{},
|
||||||
@@ -287,10 +326,12 @@ func TestAny_Inject(t *testing.T) {
|
|||||||
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
||||||
EvalPermission("settings:read", Scope("settings", Parameter(":settingsId"))),
|
EvalPermission("settings:read", Scope("settings", Parameter(":settingsId"))),
|
||||||
),
|
),
|
||||||
params: map[string]string{
|
params: ScopeParams{
|
||||||
":id": "10",
|
URLParams: map[string]string{
|
||||||
":settingsId": "3",
|
":id": "10",
|
||||||
":reportId": "1",
|
":settingsId": "3",
|
||||||
|
":reportId": "1",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
@@ -301,6 +342,26 @@ func TestAny_Inject(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
desc: "should inject field and URL param",
|
||||||
|
expected: true,
|
||||||
|
evaluator: EvalAny(
|
||||||
|
EvalPermission("orgs:read", Scope("orgs", Field("OrgID"))),
|
||||||
|
EvalPermission("orgs:read", Scope("orgs", Parameter(":orgId"))),
|
||||||
|
),
|
||||||
|
params: ScopeParams{
|
||||||
|
OrgID: 3,
|
||||||
|
URLParams: map[string]string{
|
||||||
|
":orgId": "4",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
permissions: map[string]map[string]struct{}{
|
||||||
|
"orgs:read": {
|
||||||
|
"orgs:3": struct{}{},
|
||||||
|
"orgs:4": struct{}{},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
desc: "should fail for nil params",
|
desc: "should fail for nil params",
|
||||||
expected: false,
|
expected: false,
|
||||||
@@ -308,7 +369,7 @@ func TestAny_Inject(t *testing.T) {
|
|||||||
EvalPermission("settings:read", Scope("reports", Parameter(":settingsId"))),
|
EvalPermission("settings:read", Scope("reports", Parameter(":settingsId"))),
|
||||||
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
EvalPermission("reports:read", Scope("reports", Parameter(":reportId"))),
|
||||||
),
|
),
|
||||||
params: nil,
|
params: ScopeParams{},
|
||||||
permissions: map[string]map[string]struct{}{
|
permissions: map[string]map[string]struct{}{
|
||||||
"reports:read": {
|
"reports:read": {
|
||||||
"reports:1": struct{}{},
|
"reports:1": struct{}{},
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func Middleware(ac accesscontrol.AccessControl) func(macaron.Handler, accesscont
|
|||||||
}
|
}
|
||||||
|
|
||||||
return func(c *models.ReqContext) {
|
return func(c *models.ReqContext) {
|
||||||
injected, err := evaluator.Inject(macaron.Params(c.Req))
|
injected, err := evaluator.Inject(buildScopeParams(c))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.JsonApiErr(http.StatusInternalServerError, "Internal server error", err)
|
c.JsonApiErr(http.StatusInternalServerError, "Internal server error", err)
|
||||||
return
|
return
|
||||||
@@ -69,3 +69,10 @@ func newID() string {
|
|||||||
}
|
}
|
||||||
return "ACE" + id
|
return "ACE" + id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func buildScopeParams(c *models.ReqContext) accesscontrol.ScopeParams {
|
||||||
|
return accesscontrol.ScopeParams{
|
||||||
|
OrgID: c.OrgId,
|
||||||
|
URLParams: macaron.Params(c.Req),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -140,6 +140,12 @@ func (p Permission) OSSPermission() Permission {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ScopeParams holds the parameters used to fill in scope templates
|
||||||
|
type ScopeParams struct {
|
||||||
|
OrgID int64
|
||||||
|
URLParams map[string]string
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
GlobalOrgID = 0
|
GlobalOrgID = 0
|
||||||
// Permission actions
|
// Permission actions
|
||||||
|
|||||||
@@ -18,8 +18,14 @@ func Scope(parts ...string) string {
|
|||||||
return b.String()
|
return b.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parameter returns injectable scope part
|
// Parameter returns injectable scope part, based on URL parameters.
|
||||||
// e.g. Scope("users", Parameter(":id")) or "users:" + Parameter(":id")
|
// e.g. Scope("users", Parameter(":id")) or "users:" + Parameter(":id")
|
||||||
func Parameter(key string) string {
|
func Parameter(key string) string {
|
||||||
return fmt.Sprintf(`{{ index . "%s" }}`, key)
|
return fmt.Sprintf(`{{ index .URLParams "%s" }}`, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Field returns an injectable scope part for selected fields from the request's context available in accesscontrol.ScopeParams.
|
||||||
|
// e.g. Scope("orgs", Parameter("OrgID")) or "orgs:" + Parameter("OrgID")
|
||||||
|
func Field(key string) string {
|
||||||
|
return fmt.Sprintf(`{{ .%s }}`, key)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user