From ff3c1e3144d50a0a998dcc9112ab86144e24febc Mon Sep 17 00:00:00 2001 From: Gabriel MABILLE Date: Wed, 23 Mar 2022 08:48:32 +0100 Subject: [PATCH] AccessControl: Handle ':' in attribute resolution (#46742) * AccessControl: Handle ':' in attribute resolution * Simplify based on assumption that prefixes will have maximum 2 parts --- pkg/services/accesscontrol/scope.go | 14 ++++++++---- pkg/services/accesscontrol/scope_test.go | 28 ++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 4 deletions(-) diff --git a/pkg/services/accesscontrol/scope.go b/pkg/services/accesscontrol/scope.go index 69f2533537f..8d87b0eaf5a 100644 --- a/pkg/services/accesscontrol/scope.go +++ b/pkg/services/accesscontrol/scope.go @@ -14,8 +14,9 @@ import ( ) const ( - ttl = 30 * time.Second - cleanInterval = 2 * time.Minute + ttl = 30 * time.Second + cleanInterval = 2 * time.Minute + maxPrefixParts = 2 ) func GetResourceScope(resource string, resourceID string) string { @@ -156,10 +157,15 @@ func (s *ScopeResolver) GetResolveAttributeScopeMutator(orgID int64) ScopeMutato } } +// scopePrefix returns the prefix associated to a given scope +// we assume prefixes are all in the form :: +// ex: "datasources:name:test" returns "datasources:name:" func scopePrefix(scope string) string { parts := strings.Split(scope, ":") - n := len(parts) - 1 - parts[n] = "" + // We assume prefixes don't have more than maxPrefixParts parts + if len(parts) > maxPrefixParts { + parts = append(parts[:maxPrefixParts], "") + } return strings.Join(parts, ":") } diff --git a/pkg/services/accesscontrol/scope_test.go b/pkg/services/accesscontrol/scope_test.go index a1bedaed3a6..74c58cbbcfd 100644 --- a/pkg/services/accesscontrol/scope_test.go +++ b/pkg/services/accesscontrol/scope_test.go @@ -66,6 +66,10 @@ func TestScopeResolver_ResolveAttribute(t *testing.T) { return Scope("datasources", "id", "1"), nil } else if initialScope == "datasources:name:testds2" { return Scope("datasources", "id", "2"), nil + } else if initialScope == "datasources:name:test:ds4" { + return Scope("datasources", "id", "4"), nil + } else if initialScope == "datasources:name:testds5*" { + return Scope("datasources", "id", "5"), nil } else { return "", models.ErrDataSourceNotFound } @@ -118,6 +122,20 @@ func TestScopeResolver_ResolveAttribute(t *testing.T) { ), wantCalls: 2, }, + { + name: "should resolve name with colon", + orgID: 1, + evaluator: EvalPermission("datasources:read", Scope("datasources", "name", "test:ds4")), + wantEvaluator: EvalPermission("datasources:read", Scope("datasources", "id", "4")), + wantCalls: 1, + }, + { + name: "should resolve names with '*'", + orgID: 1, + evaluator: EvalPermission("datasources:read", Scope("datasources", "name", "testds5*")), + wantEvaluator: EvalPermission("datasources:read", Scope("datasources", "id", "5")), + wantCalls: 1, + }, } for _, tt := range tests { resolver := NewScopeResolver() @@ -167,6 +185,16 @@ func Test_scopePrefix(t *testing.T) { scope: "datasources:name:testds", want: "datasources:name:", }, + { + name: "datasources with colons in name", + scope: "datasources:name:test:a::ds", + want: "datasources:name:", + }, + { + name: "prefix", + scope: "datasources:name:", + want: "datasources:name:", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {