AccessControl: Handle ':' in attribute resolution (#46742)

* AccessControl: Handle ':' in attribute resolution

* Simplify based on assumption that prefixes will have maximum 2 parts
This commit is contained in:
Gabriel MABILLE 2022-03-23 08:48:32 +01:00 committed by GitHub
parent 76c9ad2d9b
commit ff3c1e3144
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 4 deletions

View File

@ -14,8 +14,9 @@ import (
) )
const ( const (
ttl = 30 * time.Second ttl = 30 * time.Second
cleanInterval = 2 * time.Minute cleanInterval = 2 * time.Minute
maxPrefixParts = 2
) )
func GetResourceScope(resource string, resourceID string) string { 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 <resource>:<attribute>:<value>
// ex: "datasources:name:test" returns "datasources:name:"
func scopePrefix(scope string) string { func scopePrefix(scope string) string {
parts := strings.Split(scope, ":") parts := strings.Split(scope, ":")
n := len(parts) - 1 // We assume prefixes don't have more than maxPrefixParts parts
parts[n] = "" if len(parts) > maxPrefixParts {
parts = append(parts[:maxPrefixParts], "")
}
return strings.Join(parts, ":") return strings.Join(parts, ":")
} }

View File

@ -66,6 +66,10 @@ func TestScopeResolver_ResolveAttribute(t *testing.T) {
return Scope("datasources", "id", "1"), nil return Scope("datasources", "id", "1"), nil
} else if initialScope == "datasources:name:testds2" { } else if initialScope == "datasources:name:testds2" {
return Scope("datasources", "id", "2"), nil 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 { } else {
return "", models.ErrDataSourceNotFound return "", models.ErrDataSourceNotFound
} }
@ -118,6 +122,20 @@ func TestScopeResolver_ResolveAttribute(t *testing.T) {
), ),
wantCalls: 2, 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 { for _, tt := range tests {
resolver := NewScopeResolver() resolver := NewScopeResolver()
@ -167,6 +185,16 @@ func Test_scopePrefix(t *testing.T) {
scope: "datasources:name:testds", scope: "datasources:name:testds",
want: "datasources:name:", 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 { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {