mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
parent
6c7d326499
commit
bd918927b4
@ -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 {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user