Accesscontrol: fix data source name resolver and add uid name resolver (#46409)

* Fix data source scope resolver

* Adding ds UID scope resolver

* Register UID resolver

* use package full name

* even if it cannot be empty as of now and is also checked by store, better safe than sorry
This commit is contained in:
Gabriel MABILLE 2022-03-11 08:50:04 +01:00 committed by GitHub
parent 6c7d326499
commit bd918927b4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 124 additions and 17 deletions

View File

@ -83,6 +83,7 @@ func ProvideService(
s.Bus.AddHandler(s.GetDefaultDataSource)
ac.RegisterAttributeScopeResolver(NewNameScopeResolver(store))
ac.RegisterAttributeScopeResolver(NewUidScopeResolver(store))
return s
}
@ -96,16 +97,15 @@ type DataSourceRetriever interface {
// NewNameScopeResolver provides an AttributeScopeResolver able to
// translate a scope prefixed with "datasources:name:" into an id based scope.
func NewNameScopeResolver(db DataSourceRetriever) (string, accesscontrol.AttributeScopeResolveFunc) {
prefix := datasources.ScopeDatasourcesProvider.GetResourceScopeName("")
dsNameResolver := func(ctx context.Context, orgID int64, initialScope string) (string, error) {
dsNames := strings.Split(initialScope, ":")
if dsNames[0] != datasources.ScopeDatasourcesRoot || len(dsNames) != 3 {
if !strings.HasPrefix(initialScope, prefix) {
return "", accesscontrol.ErrInvalidScope
}
dsName := dsNames[2]
// Special wildcard case
if dsName == "*" {
return datasources.ScopeDatasourcesProvider.GetResourceAllIDScope(), nil
dsName := initialScope[len(prefix):]
if dsName == "" {
return "", accesscontrol.ErrInvalidScope
}
query := models.GetDataSourceQuery{Name: dsName, OrgId: orgID}
@ -113,10 +113,35 @@ func NewNameScopeResolver(db DataSourceRetriever) (string, accesscontrol.Attribu
return "", err
}
return datasources.ScopeDatasourcesProvider.GetResourceScope(fmt.Sprintf("%v", query.Result.Id)), nil
return datasources.ScopeDatasourcesProvider.GetResourceScope(strconv.FormatInt(query.Result.Id, 10)), nil
}
return datasources.ScopeDatasourcesProvider.GetResourceScopeName(""), dsNameResolver
return prefix, dsNameResolver
}
// NewUidScopeResolver provides an AttributeScopeResolver able to
// translate a scope prefixed with "datasources:uid:" into an id based scope.
func NewUidScopeResolver(db DataSourceRetriever) (string, accesscontrol.AttributeScopeResolveFunc) {
prefix := datasources.ScopeDatasourcesProvider.GetResourceScopeUID("")
dsUIDResolver := func(ctx context.Context, orgID int64, initialScope string) (string, error) {
if !strings.HasPrefix(initialScope, prefix) {
return "", accesscontrol.ErrInvalidScope
}
dsUID := initialScope[len(prefix):]
if dsUID == "" {
return "", accesscontrol.ErrInvalidScope
}
query := models.GetDataSourceQuery{Uid: dsUID, OrgId: orgID}
if err := db.GetDataSource(ctx, &query); err != nil {
return "", err
}
return datasources.ScopeDatasourcesProvider.GetResourceScope(strconv.FormatInt(query.Result.Id, 10)), nil
}
return prefix, dsUIDResolver
}
func (s *Service) GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error {

View File

@ -70,19 +70,30 @@ func TestService(t *testing.T) {
}
type dataSourceMockRetriever struct {
res *models.DataSource
res []*models.DataSource
}
func (d *dataSourceMockRetriever) GetDataSource(ctx context.Context, query *models.GetDataSourceQuery) error {
if query.Name == d.res.Name {
query.Result = d.res
for _, datasource := range d.res {
nameMatch := query.Name != "" && query.Name == datasource.Name
uidMatch := query.Uid != "" && query.Uid == datasource.Uid
if nameMatch || uidMatch {
query.Result = datasource
return nil
return nil
}
}
return models.ErrDataSourceNotFound
}
func TestService_NameScopeResolver(t *testing.T) {
retriever := &dataSourceMockRetriever{[]*models.DataSource{
{Id: 1, Name: "test-datasource"},
{Id: 2, Name: "*"},
{Id: 3, Name: ":/*"},
{Id: 4, Name: ":"},
}}
type testCaseResolver struct {
desc string
given string
@ -98,9 +109,21 @@ func TestService_NameScopeResolver(t *testing.T) {
wantErr: nil,
},
{
desc: "correct",
desc: "asterisk in name",
given: "datasources:name:*",
want: "datasources:id:*",
want: "datasources:id:2",
wantErr: nil,
},
{
desc: "complex name",
given: "datasources:name::/*",
want: "datasources:id:3",
wantErr: nil,
},
{
desc: "colon in name",
given: "datasources:name::",
want: "datasources:id:4",
wantErr: nil,
},
{
@ -115,11 +138,70 @@ func TestService_NameScopeResolver(t *testing.T) {
want: "",
wantErr: accesscontrol.ErrInvalidScope,
},
{
desc: "empty name scope",
given: "datasources:name:",
want: "",
wantErr: accesscontrol.ErrInvalidScope,
},
}
prefix, resolver := NewNameScopeResolver(retriever)
require.Equal(t, "datasources:name:", prefix)
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {
resolved, err := resolver(context.Background(), 1, tc.given)
if tc.wantErr != nil {
require.Error(t, err)
require.Equal(t, tc.wantErr, err)
} else {
require.NoError(t, err)
require.Equal(t, tc.want, resolved)
}
})
}
}
func TestService_UIDScopeResolver(t *testing.T) {
retriever := &dataSourceMockRetriever{[]*models.DataSource{
{Id: 1, Uid: "NnftN9Lnz"},
}}
type testCaseResolver struct {
desc string
given string
want string
wantErr error
}
testDataSource := &models.DataSource{Id: 1, Name: "test-datasource"}
prefix, resolver := NewNameScopeResolver(&dataSourceMockRetriever{testDataSource})
require.Equal(t, "datasources:name:", prefix)
testCases := []testCaseResolver{
{
desc: "correct",
given: "datasources:uid:NnftN9Lnz",
want: "datasources:id:1",
wantErr: nil,
},
{
desc: "unknown datasource",
given: "datasources:uid:unknown",
want: "",
wantErr: models.ErrDataSourceNotFound,
},
{
desc: "malformed scope",
given: "datasources:unknown",
want: "",
wantErr: accesscontrol.ErrInvalidScope,
},
{
desc: "empty uid scope",
given: "datasources:uid:",
want: "",
wantErr: accesscontrol.ErrInvalidScope,
},
}
prefix, resolver := NewUidScopeResolver(retriever)
require.Equal(t, "datasources:uid:", prefix)
for _, tc := range testCases {
t.Run(tc.desc, func(t *testing.T) {