2018-01-29 20:51:01 +08:00
package api
2018-02-21 01:11:50 +08:00
import (
2024-07-03 14:08:57 +08:00
"context"
2018-02-21 18:24:54 +08:00
"encoding/json"
"fmt"
2022-06-22 16:29:26 +08:00
"net/http"
2024-12-04 02:33:01 +08:00
"net/http/httptest"
2023-03-20 19:04:22 +08:00
"strings"
2018-02-21 18:24:54 +08:00
"testing"
2022-06-30 21:31:54 +08:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
2024-12-04 02:33:01 +08:00
clientrest "k8s.io/client-go/rest"
2022-06-30 21:31:54 +08:00
2018-02-21 18:24:54 +08:00
"github.com/grafana/grafana/pkg/api/dtos"
2024-12-04 02:33:01 +08:00
folderv0alpha1 "github.com/grafana/grafana/pkg/apis/folder/v0alpha1"
grafanarest "github.com/grafana/grafana/pkg/apiserver/rest"
2022-06-22 16:29:26 +08:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
2022-11-24 22:38:55 +08:00
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
2022-06-22 16:29:26 +08:00
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2024-12-04 02:33:01 +08:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2018-03-07 06:59:45 +08:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-03-03 22:05:47 +08:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-10-11 03:47:53 +08:00
"github.com/grafana/grafana/pkg/services/folder"
"github.com/grafana/grafana/pkg/services/folder/foldertest"
2023-09-08 15:43:41 +08:00
"github.com/grafana/grafana/pkg/services/guardian"
2022-07-16 00:06:44 +08:00
"github.com/grafana/grafana/pkg/services/quota/quotatest"
2023-01-30 22:17:53 +08:00
"github.com/grafana/grafana/pkg/services/search/model"
2022-08-10 17:56:48 +08:00
"github.com/grafana/grafana/pkg/services/user"
2024-12-04 02:33:01 +08:00
"github.com/grafana/grafana/pkg/services/user/usertest"
2019-03-06 15:09:34 +08:00
"github.com/grafana/grafana/pkg/setting"
2022-06-22 16:29:26 +08:00
"github.com/grafana/grafana/pkg/web/webtest"
2018-02-21 01:11:50 +08:00
)
2023-03-20 19:04:22 +08:00
func TestFoldersCreateAPIEndpoint ( t * testing . T ) {
2022-10-11 03:47:53 +08:00
folderService := & foldertest . FakeService { }
2023-03-20 19:04:22 +08:00
setUpRBACGuardian ( t )
2022-02-16 21:15:44 +08:00
2023-03-20 19:04:22 +08:00
folderWithoutParentInput := "{ \"uid\": \"uid\", \"title\": \"Folder\"}"
type testCase struct {
description string
expectedCode int
expectedFolder * folder . Folder
expectedFolderSvcError error
permissions [ ] accesscontrol . Permission
withNestedFolders bool
input string
}
tcs := [ ] testCase {
{
description : "folder creation succeeds given the correct request for creating a folder" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusOK ,
2023-12-20 22:12:05 +08:00
expectedFolder : & folder . Folder { UID : "uid" , Title : "Folder" } ,
2023-03-20 19:04:22 +08:00
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails without permissions to create a folder" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusForbidden ,
permissions : [ ] accesscontrol . Permission { } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusConflict ,
expectedFolderSvcError : dashboards . ErrFolderWithSameUIDExists ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusBadRequest ,
expectedFolderSvcError : dashboards . ErrFolderTitleEmpty ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusBadRequest ,
expectedFolderSvcError : dashboards . ErrDashboardInvalidUid ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusBadRequest ,
expectedFolderSvcError : dashboards . ErrDashboardUidTooLong ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusForbidden ,
expectedFolderSvcError : dashboards . ErrFolderAccessDenied ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusNotFound ,
expectedFolderSvcError : dashboards . ErrFolderNotFound ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
{
description : "folder creation fails given folder service error %s" ,
input : folderWithoutParentInput ,
expectedCode : http . StatusPreconditionFailed ,
expectedFolderSvcError : dashboards . ErrFolderVersionMismatch ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersCreate } } ,
} ,
}
for _ , tc := range tcs {
folderService . ExpectedFolder = tc . expectedFolder
folderService . ExpectedError = tc . expectedFolderSvcError
folderPermService := acmock . NewMockedPermissionsService ( )
folderPermService . On ( "SetPermissions" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( [ ] accesscontrol . ResourcePermission { } , nil )
srv := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
2023-08-25 23:13:46 +08:00
hs . Cfg = setting . NewCfg ( )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
if tc . withNestedFolders {
hs . Features = featuremgmt . WithFeatures ( featuremgmt . FlagNestedFolders )
}
hs . folderService = folderService
hs . folderPermissionsService = folderPermService
hs . accesscontrolService = actest . FakeService { }
} )
t . Run ( testDescription ( tc . description , tc . expectedFolderSvcError ) , func ( t * testing . T ) {
input := strings . NewReader ( tc . input )
req := srv . NewPostRequest ( "/api/folders" , input )
req = webtest . RequestWithSignedInUser ( req , userWithPermissions ( 1 , tc . permissions ) )
resp , err := srv . SendJSON ( req )
require . NoError ( t , err )
require . Equal ( t , tc . expectedCode , resp . StatusCode )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
folder := dtos . Folder { }
err = json . NewDecoder ( resp . Body ) . Decode ( & folder )
require . NoError ( t , err )
require . NoError ( t , resp . Body . Close ( ) )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
if tc . expectedCode == http . StatusOK {
2023-12-07 20:15:58 +08:00
assert . Equal ( t , "uid" , folder . UID )
2020-11-13 16:52:38 +08:00
assert . Equal ( t , "Folder" , folder . Title )
2023-03-20 19:04:22 +08:00
}
2022-10-11 03:47:53 +08:00
} )
2023-03-20 19:04:22 +08:00
}
}
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
func TestFoldersUpdateAPIEndpoint ( t * testing . T ) {
folderService := & foldertest . FakeService { }
setUpRBACGuardian ( t )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
type testCase struct {
description string
expectedCode int
expectedFolder * folder . Folder
expectedFolderSvcError error
permissions [ ] accesscontrol . Permission
}
tcs := [ ] testCase {
{
description : "folder updating succeeds given the correct request and permissions to update a folder" ,
expectedCode : http . StatusOK ,
2023-12-20 22:12:05 +08:00
expectedFolder : & folder . Folder { UID : "uid" , Title : "Folder upd" } ,
2023-03-20 19:04:22 +08:00
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails without permissions to update a folder" ,
expectedCode : http . StatusForbidden ,
permissions : [ ] accesscontrol . Permission { } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusConflict ,
expectedFolderSvcError : dashboards . ErrFolderWithSameUIDExists ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusBadRequest ,
expectedFolderSvcError : dashboards . ErrFolderTitleEmpty ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusBadRequest ,
expectedFolderSvcError : dashboards . ErrDashboardInvalidUid ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusBadRequest ,
expectedFolderSvcError : dashboards . ErrDashboardUidTooLong ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusForbidden ,
expectedFolderSvcError : dashboards . ErrFolderAccessDenied ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusNotFound ,
expectedFolderSvcError : dashboards . ErrFolderNotFound ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
{
description : "folder updating fails given folder service error %s" ,
expectedCode : http . StatusPreconditionFailed ,
expectedFolderSvcError : dashboards . ErrFolderVersionMismatch ,
permissions : [ ] accesscontrol . Permission { { Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersAll } } ,
} ,
}
for _ , tc := range tcs {
folderService . ExpectedFolder = tc . expectedFolder
folderService . ExpectedError = tc . expectedFolderSvcError
srv := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
2023-08-25 23:13:46 +08:00
hs . Cfg = setting . NewCfg ( )
2023-03-20 19:04:22 +08:00
hs . folderService = folderService
} )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
t . Run ( testDescription ( tc . description , tc . expectedFolderSvcError ) , func ( t * testing . T ) {
input := strings . NewReader ( "{ \"uid\": \"uid\", \"title\": \"Folder upd\" }" )
req := srv . NewRequest ( http . MethodPut , "/api/folders/uid" , input )
req = webtest . RequestWithSignedInUser ( req , userWithPermissions ( 1 , tc . permissions ) )
resp , err := srv . SendJSON ( req )
require . NoError ( t , err )
require . Equal ( t , tc . expectedCode , resp . StatusCode )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
folder := dtos . Folder { }
err = json . NewDecoder ( resp . Body ) . Decode ( & folder )
require . NoError ( t , err )
require . NoError ( t , resp . Body . Close ( ) )
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
if tc . expectedCode == http . StatusOK {
2023-12-07 20:15:58 +08:00
assert . Equal ( t , "uid" , folder . UID )
2020-11-13 16:52:38 +08:00
assert . Equal ( t , "Folder upd" , folder . Title )
2023-03-20 19:04:22 +08:00
}
} )
}
}
2018-02-21 18:24:54 +08:00
2023-03-20 19:04:22 +08:00
func testDescription ( description string , expectedErr error ) string {
if expectedErr != nil {
return fmt . Sprintf ( description , expectedErr . Error ( ) )
} else {
return description
}
2018-02-21 18:24:54 +08:00
}
2022-06-22 16:29:26 +08:00
func TestHTTPServer_FolderMetadata ( t * testing . T ) {
setUpRBACGuardian ( t )
2022-10-11 03:47:53 +08:00
folderService := & foldertest . FakeService { }
2023-04-21 22:05:11 +08:00
features := featuremgmt . WithFeatures ( featuremgmt . FlagNestedFolders )
2022-06-22 16:29:26 +08:00
server := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
2023-08-25 23:13:46 +08:00
hs . Cfg = setting . NewCfg ( )
2022-06-22 16:29:26 +08:00
hs . folderService = folderService
2022-11-15 03:08:10 +08:00
hs . QuotaService = quotatest . New ( false , nil )
2023-01-23 20:09:09 +08:00
hs . SearchService = & mockSearchService {
2023-01-30 22:17:53 +08:00
ExpectedResult : model . HitList { } ,
2023-01-23 20:09:09 +08:00
}
2023-04-21 22:05:11 +08:00
hs . Features = features
2022-06-22 16:29:26 +08:00
} )
2023-04-21 22:05:11 +08:00
t . Run ( "Should attach access control metadata to folder response" , func ( t * testing . T ) {
folderService . ExpectedFolder = & folder . Folder { UID : "folderUid" }
2022-06-22 16:29:26 +08:00
2023-04-21 22:05:11 +08:00
req := server . NewGetRequest ( "/api/folders/folderUid?accesscontrol=true" )
2022-08-11 19:28:55 +08:00
webtest . RequestWithSignedInUser ( req , & user . SignedInUser { UserID : 1 , OrgID : 1 , Permissions : map [ int64 ] map [ string ] [ ] string {
2024-07-03 14:08:57 +08:00
1 : accesscontrol . GroupScopesByActionContext ( context . Background ( ) , [ ] accesscontrol . Permission {
2022-06-22 16:29:26 +08:00
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersAll } ,
2023-04-21 22:05:11 +08:00
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "folderUid" ) } ,
2022-06-22 16:29:26 +08:00
} ) ,
} } )
res , err := server . Send ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
2023-04-21 22:05:11 +08:00
defer func ( ) { require . NoError ( t , res . Body . Close ( ) ) } ( )
2022-06-22 16:29:26 +08:00
2023-04-21 22:05:11 +08:00
body := dtos . Folder { }
2022-06-22 16:29:26 +08:00
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & body ) )
2023-04-21 22:05:11 +08:00
assert . True ( t , body . AccessControl [ dashboards . ActionFoldersRead ] )
assert . True ( t , body . AccessControl [ dashboards . ActionFoldersWrite ] )
2022-06-22 16:29:26 +08:00
} )
2023-04-21 22:05:11 +08:00
t . Run ( "Should attach access control metadata to folder response with permissions cascading from nested folders" , func ( t * testing . T ) {
2022-11-10 17:41:03 +08:00
folderService . ExpectedFolder = & folder . Folder { UID : "folderUid" }
2023-04-21 22:05:11 +08:00
folderService . ExpectedFolders = [ ] * folder . Folder { { UID : "parentUid" } }
features = featuremgmt . WithFeatures ( featuremgmt . FlagNestedFolders )
defer func ( ) {
features = featuremgmt . WithFeatures ( )
folderService . ExpectedFolders = nil
} ( )
2022-06-22 16:29:26 +08:00
req := server . NewGetRequest ( "/api/folders/folderUid?accesscontrol=true" )
2022-08-11 19:28:55 +08:00
webtest . RequestWithSignedInUser ( req , & user . SignedInUser { UserID : 1 , OrgID : 1 , Permissions : map [ int64 ] map [ string ] [ ] string {
2024-07-03 14:08:57 +08:00
1 : accesscontrol . GroupScopesByActionContext ( context . Background ( ) , [ ] accesscontrol . Permission {
2022-06-22 16:29:26 +08:00
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersAll } ,
2023-04-21 22:05:11 +08:00
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "parentUid" ) } ,
{ Action : dashboards . ActionDashboardsCreate , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "folderUid" ) } ,
2022-06-22 16:29:26 +08:00
} ) ,
} } )
res , err := server . Send ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
defer func ( ) { require . NoError ( t , res . Body . Close ( ) ) } ( )
body := dtos . Folder { }
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & body ) )
assert . True ( t , body . AccessControl [ dashboards . ActionFoldersRead ] )
assert . True ( t , body . AccessControl [ dashboards . ActionFoldersWrite ] )
2023-04-21 22:05:11 +08:00
assert . True ( t , body . AccessControl [ dashboards . ActionDashboardsCreate ] )
2022-06-22 16:29:26 +08:00
} )
2023-04-21 22:05:11 +08:00
t . Run ( "Should not attach access control metadata to folder response" , func ( t * testing . T ) {
2022-11-10 17:41:03 +08:00
folderService . ExpectedFolder = & folder . Folder { UID : "folderUid" }
2022-06-22 16:29:26 +08:00
req := server . NewGetRequest ( "/api/folders/folderUid" )
2022-08-11 19:28:55 +08:00
webtest . RequestWithSignedInUser ( req , & user . SignedInUser { UserID : 1 , OrgID : 1 , Permissions : map [ int64 ] map [ string ] [ ] string {
2024-07-03 14:08:57 +08:00
1 : accesscontrol . GroupScopesByActionContext ( context . Background ( ) , [ ] accesscontrol . Permission {
2022-06-22 16:29:26 +08:00
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersAll } ,
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "folderUid" ) } ,
} ) ,
} } )
res , err := server . Send ( req )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
defer func ( ) { require . NoError ( t , res . Body . Close ( ) ) } ( )
body := dtos . Folder { }
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & body ) )
assert . False ( t , body . AccessControl [ dashboards . ActionFoldersRead ] )
assert . False ( t , body . AccessControl [ dashboards . ActionFoldersWrite ] )
} )
}
2023-03-20 19:04:22 +08:00
func TestFolderMoveAPIEndpoint ( t * testing . T ) {
2023-03-30 16:46:11 +08:00
folderService := & foldertest . FakeService {
ExpectedFolder : & folder . Folder { } ,
}
2022-06-22 16:29:26 +08:00
setUpRBACGuardian ( t )
2019-03-06 15:09:34 +08:00
2023-03-20 19:04:22 +08:00
type testCase struct {
description string
expectedCode int
permissions [ ] accesscontrol . Permission
newParentUid string
}
tcs := [ ] testCase {
{
description : "can move folder to another folder with specific permissions" ,
newParentUid : "newParentUid" ,
expectedCode : http . StatusOK ,
permissions : [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "uid" ) } ,
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "newParentUid" ) } ,
} ,
} ,
2023-03-30 16:46:11 +08:00
{
description : "can move folder to the root folder with specific permissions" ,
newParentUid : "" ,
expectedCode : http . StatusOK ,
permissions : [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "uid" ) } ,
} ,
} ,
2023-03-20 19:04:22 +08:00
{
description : "forbidden to move folder to another folder without the write access on the folder being moved" ,
newParentUid : "newParentUid" ,
expectedCode : http . StatusForbidden ,
permissions : [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersWrite , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "newParentUid" ) } ,
} ,
} ,
}
for _ , tc := range tcs {
srv := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
2023-08-25 23:13:46 +08:00
hs . Cfg = setting . NewCfg ( )
2023-03-20 19:04:22 +08:00
hs . Features = featuremgmt . WithFeatures ( featuremgmt . FlagNestedFolders )
hs . folderService = folderService
2018-02-21 18:24:54 +08:00
} )
2023-03-20 19:04:22 +08:00
t . Run ( tc . description , func ( t * testing . T ) {
input := strings . NewReader ( fmt . Sprintf ( "{ \"parentUid\": \"%s\"}" , tc . newParentUid ) )
req := srv . NewRequest ( http . MethodPost , "/api/folders/uid/move" , input )
req = webtest . RequestWithSignedInUser ( req , userWithPermissions ( 1 , tc . permissions ) )
resp , err := srv . SendJSON ( req )
require . NoError ( t , err )
require . Equal ( t , tc . expectedCode , resp . StatusCode )
require . NoError ( t , resp . Body . Close ( ) )
2018-02-21 18:24:54 +08:00
} )
2023-03-20 19:04:22 +08:00
}
2018-02-21 18:24:54 +08:00
}
2023-04-25 16:22:20 +08:00
func TestFolderGetAPIEndpoint ( t * testing . T ) {
folderService := & foldertest . FakeService {
ExpectedFolder : & folder . Folder {
UID : "uid" ,
Title : "uid title" ,
} ,
ExpectedFolders : [ ] * folder . Folder {
{
UID : "parent" ,
Title : "parent title" ,
} ,
{
UID : "subfolder" ,
Title : "subfolder title" ,
} ,
} ,
}
type testCase struct {
description string
URL string
2024-01-10 02:38:06 +08:00
features featuremgmt . FeatureToggles
2023-04-25 16:22:20 +08:00
expectedCode int
expectedParentUIDs [ ] string
2023-12-07 20:56:04 +08:00
expectedParentOrgIDs [ ] int64
2023-04-25 16:22:20 +08:00
expectedParentTitles [ ] string
permissions [ ] accesscontrol . Permission
2023-09-08 15:43:41 +08:00
g * guardian . FakeDashboardGuardian
2023-04-25 16:22:20 +08:00
}
tcs := [ ] testCase {
{
description : "get folder by UID should return parent folders if nested folder are enabled" ,
URL : "/api/folders/uid" ,
expectedCode : http . StatusOK ,
features : featuremgmt . WithFeatures ( featuremgmt . FlagNestedFolders ) ,
expectedParentUIDs : [ ] string { "parent" , "subfolder" } ,
2023-12-07 20:56:04 +08:00
expectedParentOrgIDs : [ ] int64 { 0 , 0 } ,
2023-04-25 16:22:20 +08:00
expectedParentTitles : [ ] string { "parent title" , "subfolder title" } ,
permissions : [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "uid" ) } ,
} ,
2023-09-08 15:43:41 +08:00
g : & guardian . FakeDashboardGuardian { CanViewValue : true } ,
} ,
{
description : "get folder by UID should return parent folders redacted if nested folder are enabled and user does not have read access to parent folders" ,
URL : "/api/folders/uid" ,
expectedCode : http . StatusOK ,
features : featuremgmt . WithFeatures ( featuremgmt . FlagNestedFolders ) ,
expectedParentUIDs : [ ] string { REDACTED , REDACTED } ,
2023-12-07 20:56:04 +08:00
expectedParentOrgIDs : [ ] int64 { 0 , 0 } ,
2023-09-08 15:43:41 +08:00
expectedParentTitles : [ ] string { REDACTED , REDACTED } ,
permissions : [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "uid" ) } ,
} ,
g : & guardian . FakeDashboardGuardian { CanViewValue : false } ,
2023-04-25 16:22:20 +08:00
} ,
{
description : "get folder by UID should not return parent folders if nested folder are disabled" ,
URL : "/api/folders/uid" ,
expectedCode : http . StatusOK ,
features : featuremgmt . WithFeatures ( ) ,
expectedParentUIDs : [ ] string { } ,
2023-12-07 20:56:04 +08:00
expectedParentOrgIDs : [ ] int64 { 0 , 0 } ,
2023-04-25 16:22:20 +08:00
expectedParentTitles : [ ] string { } ,
permissions : [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersProvider . GetResourceScopeUID ( "uid" ) } ,
} ,
2023-09-08 15:43:41 +08:00
g : & guardian . FakeDashboardGuardian { CanViewValue : true } ,
2023-04-25 16:22:20 +08:00
} ,
}
for _ , tc := range tcs {
srv := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
2023-08-25 23:13:46 +08:00
hs . Cfg = setting . NewCfg ( )
2023-04-25 16:22:20 +08:00
hs . Features = tc . features
hs . folderService = folderService
} )
t . Run ( tc . description , func ( t * testing . T ) {
2023-09-08 15:43:41 +08:00
origNewGuardian := guardian . New
t . Cleanup ( func ( ) {
guardian . New = origNewGuardian
} )
guardian . MockDashboardGuardian ( tc . g )
2023-04-25 16:22:20 +08:00
req := srv . NewGetRequest ( tc . URL )
req = webtest . RequestWithSignedInUser ( req , userWithPermissions ( 1 , tc . permissions ) )
resp , err := srv . Send ( req )
require . NoError ( t , err )
require . Equal ( t , tc . expectedCode , resp . StatusCode )
folder := dtos . Folder { }
err = json . NewDecoder ( resp . Body ) . Decode ( & folder )
require . NoError ( t , err )
require . Equal ( t , len ( folder . Parents ) , len ( tc . expectedParentUIDs ) )
require . Equal ( t , len ( folder . Parents ) , len ( tc . expectedParentTitles ) )
for i := 0 ; i < len ( tc . expectedParentUIDs ) ; i ++ {
2023-12-07 20:15:58 +08:00
assert . Equal ( t , tc . expectedParentUIDs [ i ] , folder . Parents [ i ] . UID )
2023-12-07 20:56:04 +08:00
assert . Equal ( t , tc . expectedParentOrgIDs [ i ] , folder . Parents [ i ] . OrgID )
2023-04-25 16:22:20 +08:00
assert . Equal ( t , tc . expectedParentTitles [ i ] , folder . Parents [ i ] . Title )
}
require . NoError ( t , resp . Body . Close ( ) )
} )
}
}
2024-12-04 02:33:01 +08:00
type mockClientConfigProvider struct {
host string
}
func ( m mockClientConfigProvider ) GetDirectRestConfig ( c * contextmodel . ReqContext ) * clientrest . Config {
return & clientrest . Config {
Host : m . host ,
}
}
func ( m mockClientConfigProvider ) DirectlyServeHTTP ( w http . ResponseWriter , r * http . Request ) { }
2024-12-19 09:31:55 +08:00
// for now, test only the general folder
func TestGetFolderLegacyAndUnifiedStorage ( t * testing . T ) {
testuser := & user . User { ID : 99 , UID : "fdxsqt7t5ryf4a" , Login : "testuser" }
legacyFolder := * folder . RootFolder
expectedFolder := dtos . Folder {
UID : legacyFolder . UID ,
OrgID : 0 ,
Title : legacyFolder . Title ,
URL : legacyFolder . URL ,
HasACL : false ,
CanSave : false ,
CanEdit : true ,
CanAdmin : false ,
CanDelete : false ,
CreatedBy : "Anonymous" ,
UpdatedBy : "Anonymous" ,
}
mux := http . NewServeMux ( )
folderApiServerMock := httptest . NewServer ( mux )
defer folderApiServerMock . Close ( )
t . Run ( "happy path" , func ( t * testing . T ) {
type testCase struct {
description string
folderUID string
legacyFolder folder . Folder
expectedFolder dtos . Folder
expectedFolderServiceError error
unifiedStorageEnabled bool
unifiedStorageMode grafanarest . DualWriterMode
expectedCode int
}
tcs := [ ] testCase {
{
description : "General folder - Legacy" ,
expectedCode : http . StatusOK ,
legacyFolder : legacyFolder ,
folderUID : legacyFolder . UID ,
expectedFolder : expectedFolder ,
unifiedStorageEnabled : false ,
} ,
{
description : "General folder - Unified storage, mode 1" ,
expectedCode : http . StatusOK ,
legacyFolder : legacyFolder ,
folderUID : legacyFolder . UID ,
expectedFolder : expectedFolder ,
unifiedStorageEnabled : true ,
unifiedStorageMode : grafanarest . Mode1 ,
} ,
{
description : "General folder - Unified storage, mode 2" ,
expectedCode : http . StatusOK ,
legacyFolder : legacyFolder ,
folderUID : legacyFolder . UID ,
expectedFolder : expectedFolder ,
unifiedStorageEnabled : true ,
unifiedStorageMode : grafanarest . Mode2 ,
} ,
{
description : "General folder - Unified storage, mode 3" ,
expectedCode : http . StatusOK ,
legacyFolder : legacyFolder ,
folderUID : legacyFolder . UID ,
expectedFolder : expectedFolder ,
unifiedStorageEnabled : true ,
unifiedStorageMode : grafanarest . Mode3 ,
} ,
{
description : "General folder - Unified storage, mode 4" ,
expectedCode : http . StatusOK ,
legacyFolder : legacyFolder ,
folderUID : legacyFolder . UID ,
expectedFolder : expectedFolder ,
unifiedStorageEnabled : true ,
unifiedStorageMode : grafanarest . Mode4 ,
} ,
}
for _ , tc := range tcs {
t . Run ( tc . description , func ( t * testing . T ) {
setUpRBACGuardian ( t )
cfg := setting . NewCfg ( )
cfg . UnifiedStorage = map [ string ] setting . UnifiedStorageConfig {
folderv0alpha1 . RESOURCEGROUP : {
DualWriterMode : tc . unifiedStorageMode ,
} ,
}
featuresArr := [ ] any { featuremgmt . FlagNestedFolders }
if tc . unifiedStorageEnabled {
2025-02-19 07:11:26 +08:00
featuresArr = append ( featuresArr , featuremgmt . FlagKubernetesClientDashboardsFolders )
2024-12-19 09:31:55 +08:00
}
server := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
hs . Cfg = cfg
hs . folderService = & foldertest . FakeService {
ExpectedFolder : & tc . legacyFolder ,
ExpectedError : tc . expectedFolderServiceError ,
}
hs . QuotaService = quotatest . New ( false , nil )
hs . SearchService = & mockSearchService {
ExpectedResult : model . HitList { } ,
}
hs . userService = & usertest . FakeUserService {
ExpectedUser : testuser ,
}
hs . Features = featuremgmt . WithFeatures (
featuresArr ... ,
)
hs . clientConfigProvider = mockClientConfigProvider {
host : folderApiServerMock . URL ,
}
} )
req := server . NewRequest ( http . MethodGet , fmt . Sprintf ( "/api/folders/%s" , tc . folderUID ) , nil )
req . Header . Set ( "Content-Type" , "application/json" )
webtest . RequestWithSignedInUser ( req , & user . SignedInUser { UserID : 1 , OrgID : 1 , Permissions : map [ int64 ] map [ string ] [ ] string {
1 : accesscontrol . GroupScopesByActionContext ( context . Background ( ) , [ ] accesscontrol . Permission {
{ Action : dashboards . ActionFoldersRead , Scope : dashboards . ScopeFoldersAll } ,
} ) ,
} } )
res , err := server . Send ( req )
require . NoError ( t , err )
require . Equal ( t , tc . expectedCode , res . StatusCode )
defer func ( ) { require . NoError ( t , res . Body . Close ( ) ) } ( )
if tc . expectedCode == http . StatusOK {
body := dtos . Folder { }
require . NoError ( t , json . NewDecoder ( res . Body ) . Decode ( & body ) )
//nolint:staticcheck
body . ID = 0
body . Version = 0
tc . expectedFolder . Version = 0
require . Equal ( t , tc . expectedFolder , body )
}
} )
}
} )
}