2018-02-20 11:11:50 -06:00
package api
import (
2020-11-24 05:10:32 -06:00
"encoding/json"
2020-11-13 02:52:38 -06:00
"fmt"
2018-02-20 11:11:50 -06:00
"testing"
2021-09-23 10:43:32 -05:00
"github.com/stretchr/testify/assert"
2022-03-10 11:19:50 -06:00
"github.com/stretchr/testify/mock"
2022-05-10 08:48:47 -05:00
"github.com/stretchr/testify/require"
2022-03-10 11:19:50 -06:00
2018-02-20 11:11:50 -06:00
"github.com/grafana/grafana/pkg/api/dtos"
2021-01-15 07:43:20 -06:00
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
2023-01-18 09:01:25 -06:00
"github.com/grafana/grafana/pkg/infra/db/dbtest"
2022-05-10 08:48:47 -05:00
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2023-01-27 01:50:36 -06:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2018-02-20 11:11:50 -06:00
"github.com/grafana/grafana/pkg/services/dashboards"
2022-05-17 13:52:22 -05:00
service "github.com/grafana/grafana/pkg/services/dashboards/service"
2022-03-03 08:05:47 -06:00
"github.com/grafana/grafana/pkg/services/featuremgmt"
2022-11-10 03:41:03 -06:00
"github.com/grafana/grafana/pkg/services/folder"
2022-10-10 14:47:53 -05:00
"github.com/grafana/grafana/pkg/services/folder/foldertest"
2018-02-20 11:11:50 -06:00
"github.com/grafana/grafana/pkg/services/guardian"
2022-08-10 04:56:48 -05:00
"github.com/grafana/grafana/pkg/services/org"
2020-11-24 05:10:32 -06:00
"github.com/grafana/grafana/pkg/setting"
2018-02-20 11:11:50 -06:00
)
2020-11-13 02:52:38 -06:00
func TestFolderPermissionAPIEndpoint ( t * testing . T ) {
2020-11-24 05:10:32 -06:00
settings := setting . NewCfg ( )
2022-03-03 08:05:47 -06:00
2022-10-10 14:47:53 -05:00
folderService := & foldertest . FakeService { }
2020-11-24 05:10:32 -06:00
2022-03-10 11:19:50 -06:00
dashboardStore := & dashboards . FakeDashboardStore { }
2022-02-16 07:15:44 -06:00
defer dashboardStore . AssertExpectations ( t )
2020-11-13 02:52:38 -06:00
2022-03-10 05:58:18 -06:00
features := featuremgmt . WithFeatures ( )
2022-06-07 04:02:20 -05:00
ac := accesscontrolmock . New ( )
2022-05-10 08:48:47 -05:00
folderPermissions := accesscontrolmock . NewMockedPermissionsService ( )
dashboardPermissions := accesscontrolmock . NewMockedPermissionsService ( )
2022-03-10 05:58:18 -06:00
hs := & HTTPServer {
2022-05-10 08:48:47 -05:00
Cfg : settings ,
Features : features ,
folderService : folderService ,
folderPermissionsService : folderPermissions ,
dashboardPermissionsService : dashboardPermissions ,
2023-03-08 13:37:45 -06:00
DashboardService : service . ProvideDashboardServiceImpl (
2023-03-03 09:56:33 -06:00
settings , dashboardStore , foldertest . NewFakeFolderStore ( t ) , nil , features , folderPermissions , dashboardPermissions , ac ,
2023-01-26 02:21:10 -06:00
folderService ,
2022-03-10 05:58:18 -06:00
) ,
2022-04-25 03:42:09 -05:00
AccessControl : accesscontrolmock . New ( ) . WithDisabled ( ) ,
2022-03-10 05:58:18 -06:00
}
2022-02-16 07:15:44 -06:00
t . Run ( "Given folder not exists" , func ( t * testing . T ) {
2022-10-10 14:47:53 -05:00
t . Cleanup ( func ( ) {
folderService . ExpectedError = nil
} )
folderService . ExpectedError = dashboards . ErrFolderNotFound
2023-01-18 09:01:25 -06:00
mockSQLStore := dbtest . NewFakeDB ( )
2022-08-10 04:56:48 -05:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , org . RoleEditor , func ( sc * scenarioContext ) {
2020-11-24 05:10:32 -06:00
callGetFolderPermissions ( sc , hs )
2020-11-13 02:52:38 -06:00
assert . Equal ( t , 404 , sc . resp . Code )
2022-02-03 02:20:20 -06:00
} , mockSQLStore )
2020-11-13 02:52:38 -06:00
2022-07-18 08:14:58 -05:00
cmd := dtos . UpdateDashboardACLCommand {
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-13 02:52:38 -06:00
} ,
}
2020-11-24 05:10:32 -06:00
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
2021-03-17 10:06:10 -05:00
callUpdateFolderPermissions ( t , sc )
2020-11-24 05:10:32 -06:00
assert . Equal ( t , 404 , sc . resp . Code )
} ,
} , hs )
2020-11-13 02:52:38 -06:00
} )
t . Run ( "Given user has no admin permissions" , func ( t * testing . T ) {
origNewGuardian := guardian . New
t . Cleanup ( func ( ) {
guardian . New = origNewGuardian
2022-10-10 14:47:53 -05:00
folderService . ExpectedError = nil
2020-11-13 02:52:38 -06:00
} )
guardian . MockDashboardGuardian ( & guardian . FakeDashboardGuardian { CanAdminValue : false } )
2022-10-10 14:47:53 -05:00
folderService . ExpectedError = dashboards . ErrFolderAccessDenied
2023-01-18 09:01:25 -06:00
mockSQLStore := dbtest . NewFakeDB ( )
2022-02-16 07:15:44 -06:00
2022-08-10 04:56:48 -05:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , org . RoleEditor , func ( sc * scenarioContext ) {
2020-11-24 05:10:32 -06:00
callGetFolderPermissions ( sc , hs )
2020-11-13 02:52:38 -06:00
assert . Equal ( t , 403 , sc . resp . Code )
2022-02-03 02:20:20 -06:00
} , mockSQLStore )
2020-11-13 02:52:38 -06:00
2022-07-18 08:14:58 -05:00
cmd := dtos . UpdateDashboardACLCommand {
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-13 02:52:38 -06:00
} ,
}
2020-11-24 05:10:32 -06:00
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
2021-03-17 10:06:10 -05:00
callUpdateFolderPermissions ( t , sc )
2020-11-24 05:10:32 -06:00
assert . Equal ( t , 403 , sc . resp . Code )
} ,
} , hs )
2020-11-13 02:52:38 -06:00
} )
t . Run ( "Given user has admin permissions and permissions to update" , func ( t * testing . T ) {
origNewGuardian := guardian . New
t . Cleanup ( func ( ) {
guardian . New = origNewGuardian
} )
guardian . MockDashboardGuardian ( & guardian . FakeDashboardGuardian {
CanAdminValue : true ,
CheckPermissionBeforeUpdateValue : true ,
2023-01-20 07:58:47 -06:00
GetACLValue : [ ] * dashboards . DashboardACLInfoDTO {
2023-01-26 07:46:30 -06:00
{ OrgID : 1 , DashboardID : 1 , UserID : 2 , Permission : dashboards . PERMISSION_VIEW } ,
{ OrgID : 1 , DashboardID : 1 , UserID : 3 , Permission : dashboards . PERMISSION_EDIT } ,
{ OrgID : 1 , DashboardID : 1 , UserID : 4 , Permission : dashboards . PERMISSION_ADMIN } ,
{ OrgID : 1 , DashboardID : 1 , TeamID : 1 , Permission : dashboards . PERMISSION_VIEW } ,
{ OrgID : 1 , DashboardID : 1 , TeamID : 2 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-13 02:52:38 -06:00
} ,
} )
2022-11-10 03:41:03 -06:00
folderService . ExpectedFolder = & folder . Folder { ID : 1 , UID : "uid" , Title : "Folder" }
2022-02-16 07:15:44 -06:00
dashboardStore . On ( "UpdateDashboardACL" , mock . Anything , mock . Anything , mock . Anything ) . Return ( nil ) . Once ( )
2023-01-18 09:01:25 -06:00
mockSQLStore := dbtest . NewFakeDB ( )
2022-02-16 07:15:44 -06:00
2022-08-10 04:56:48 -05:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , org . RoleAdmin , func ( sc * scenarioContext ) {
2020-11-24 05:10:32 -06:00
callGetFolderPermissions ( sc , hs )
2020-11-13 02:52:38 -06:00
assert . Equal ( t , 200 , sc . resp . Code )
2020-11-24 05:10:32 -06:00
2023-01-20 07:58:47 -06:00
var resp [ ] * dashboards . DashboardACLInfoDTO
2020-11-24 05:10:32 -06:00
err := json . Unmarshal ( sc . resp . Body . Bytes ( ) , & resp )
2020-11-13 02:52:38 -06:00
require . NoError ( t , err )
2020-11-24 05:10:32 -06:00
assert . Len ( t , resp , 5 )
2023-01-20 07:58:47 -06:00
assert . Equal ( t , int64 ( 2 ) , resp [ 0 ] . UserID )
2023-01-26 07:46:30 -06:00
assert . Equal ( t , dashboards . PERMISSION_VIEW , resp [ 0 ] . Permission )
2022-02-03 02:20:20 -06:00
} , mockSQLStore )
2020-11-13 02:52:38 -06:00
2022-07-18 08:14:58 -05:00
cmd := dtos . UpdateDashboardACLCommand {
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-13 02:52:38 -06:00
} ,
}
2020-11-24 05:10:32 -06:00
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
2021-03-17 10:06:10 -05:00
callUpdateFolderPermissions ( t , sc )
2020-11-24 05:10:32 -06:00
assert . Equal ( t , 200 , sc . resp . Code )
var resp struct {
ID int64
Title string
}
err := json . Unmarshal ( sc . resp . Body . Bytes ( ) , & resp )
require . NoError ( t , err )
assert . Equal ( t , int64 ( 1 ) , resp . ID )
assert . Equal ( t , "Folder" , resp . Title )
} ,
} , hs )
2020-11-13 02:52:38 -06:00
} )
t . Run ( "When trying to update permissions with duplicate permissions" , func ( t * testing . T ) {
origNewGuardian := guardian . New
t . Cleanup ( func ( ) {
guardian . New = origNewGuardian
} )
guardian . MockDashboardGuardian ( & guardian . FakeDashboardGuardian {
CanAdminValue : true ,
CheckPermissionBeforeUpdateValue : false ,
CheckPermissionBeforeUpdateError : guardian . ErrGuardianPermissionExists ,
} )
2022-11-10 03:41:03 -06:00
folderService . ExpectedFolder = & folder . Folder { ID : 1 , UID : "uid" , Title : "Folder" }
2020-11-13 02:52:38 -06:00
2022-07-18 08:14:58 -05:00
cmd := dtos . UpdateDashboardACLCommand {
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-13 02:52:38 -06:00
} ,
}
2020-11-24 05:10:32 -06:00
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
2021-03-17 10:06:10 -05:00
callUpdateFolderPermissions ( t , sc )
2020-11-24 05:10:32 -06:00
assert . Equal ( t , 400 , sc . resp . Code )
} ,
} , hs )
2020-11-13 02:52:38 -06:00
} )
2020-11-18 08:36:41 -06:00
t . Run ( "When trying to update team or user permissions with a role" , func ( t * testing . T ) {
2022-08-10 04:56:48 -05:00
role := org . RoleAdmin
2022-07-18 08:14:58 -05:00
cmds := [ ] dtos . UpdateDashboardACLCommand {
2020-11-18 08:36:41 -06:00
{
2022-07-18 08:14:58 -05:00
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN , Role : & role } ,
2020-11-18 08:36:41 -06:00
} ,
} ,
{
2022-07-18 08:14:58 -05:00
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ TeamID : 1000 , Permission : dashboards . PERMISSION_ADMIN , Role : & role } ,
2020-11-18 08:36:41 -06:00
} ,
} ,
}
for _ , cmd := range cmds {
2020-11-24 05:10:32 -06:00
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
2021-03-17 10:06:10 -05:00
callUpdateFolderPermissions ( t , sc )
2020-11-18 08:36:41 -06:00
assert . Equal ( t , 400 , sc . resp . Code )
respJSON , err := jsonMap ( sc . resp . Body . Bytes ( ) )
require . NoError ( t , err )
2023-01-26 07:46:30 -06:00
assert . Equal ( t , dashboards . ErrPermissionsWithRoleNotAllowed . Error ( ) , respJSON [ "error" ] )
2020-11-24 05:10:32 -06:00
} ,
} , hs )
2020-11-18 08:36:41 -06:00
}
} )
2020-11-13 02:52:38 -06:00
t . Run ( "When trying to override inherited permissions with lower precedence" , func ( t * testing . T ) {
origNewGuardian := guardian . New
t . Cleanup ( func ( ) {
guardian . New = origNewGuardian
} )
guardian . MockDashboardGuardian ( & guardian . FakeDashboardGuardian {
CanAdminValue : true ,
CheckPermissionBeforeUpdateValue : false ,
CheckPermissionBeforeUpdateError : guardian . ErrGuardianOverride } ,
)
2022-11-10 03:41:03 -06:00
folderService . ExpectedFolder = & folder . Folder { ID : 1 , UID : "uid" , Title : "Folder" }
2020-11-13 02:52:38 -06:00
2022-07-18 08:14:58 -05:00
cmd := dtos . UpdateDashboardACLCommand {
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-13 02:52:38 -06:00
} ,
}
2020-11-24 05:10:32 -06:00
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
2021-03-17 10:06:10 -05:00
callUpdateFolderPermissions ( t , sc )
2020-11-24 05:10:32 -06:00
assert . Equal ( t , 400 , sc . resp . Code )
} ,
} , hs )
} )
t . Run ( "Getting and updating folder permissions with hidden users" , func ( t * testing . T ) {
origNewGuardian := guardian . New
settings . HiddenUsers = map [ string ] struct { } {
"hiddenUser" : { } ,
testUserLogin : { } ,
}
t . Cleanup ( func ( ) {
guardian . New = origNewGuardian
settings . HiddenUsers = make ( map [ string ] struct { } )
2018-02-26 13:14:21 -06:00
} )
2020-11-24 05:10:32 -06:00
guardian . MockDashboardGuardian ( & guardian . FakeDashboardGuardian {
CanAdminValue : true ,
CheckPermissionBeforeUpdateValue : true ,
2023-01-20 07:58:47 -06:00
GetACLValue : [ ] * dashboards . DashboardACLInfoDTO {
2023-01-26 07:46:30 -06:00
{ OrgID : 1 , DashboardID : 1 , UserID : 2 , UserLogin : "hiddenUser" , Permission : dashboards . PERMISSION_VIEW } ,
{ OrgID : 1 , DashboardID : 1 , UserID : 3 , UserLogin : testUserLogin , Permission : dashboards . PERMISSION_EDIT } ,
{ OrgID : 1 , DashboardID : 1 , UserID : 4 , UserLogin : "user_1" , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-24 05:10:32 -06:00
} ,
2023-01-20 07:58:47 -06:00
GetHiddenACLValue : [ ] * dashboards . DashboardACL {
2023-01-26 07:46:30 -06:00
{ OrgID : 1 , DashboardID : 1 , UserID : 2 , Permission : dashboards . PERMISSION_VIEW } ,
2020-11-24 05:10:32 -06:00
} ,
} )
2023-01-20 07:58:47 -06:00
var gotItems [ ] * dashboards . DashboardACL
2020-11-24 05:10:32 -06:00
2022-11-10 03:41:03 -06:00
folderService . ExpectedFolder = & folder . Folder { ID : 1 , UID : "uid" , Title : "Folder" }
2022-02-16 07:15:44 -06:00
dashboardStore . On ( "UpdateDashboardACL" , mock . Anything , mock . Anything , mock . Anything ) . Run ( func ( args mock . Arguments ) {
2023-01-20 07:58:47 -06:00
gotItems = args . Get ( 2 ) . ( [ ] * dashboards . DashboardACL )
2022-02-16 07:15:44 -06:00
} ) . Return ( nil ) . Once ( )
2020-11-24 05:10:32 -06:00
2023-01-20 07:58:47 -06:00
var resp [ ] * dashboards . DashboardACLInfoDTO
2023-01-18 09:01:25 -06:00
mockSQLStore := dbtest . NewFakeDB ( )
2022-08-10 04:56:48 -05:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , org . RoleAdmin , func ( sc * scenarioContext ) {
2020-11-24 05:10:32 -06:00
callGetFolderPermissions ( sc , hs )
assert . Equal ( t , 200 , sc . resp . Code )
err := json . Unmarshal ( sc . resp . Body . Bytes ( ) , & resp )
require . NoError ( t , err )
assert . Len ( t , resp , 2 )
2023-01-20 07:58:47 -06:00
assert . Equal ( t , int64 ( 3 ) , resp [ 0 ] . UserID )
2023-01-26 07:46:30 -06:00
assert . Equal ( t , dashboards . PERMISSION_EDIT , resp [ 0 ] . Permission )
2023-01-20 07:58:47 -06:00
assert . Equal ( t , int64 ( 4 ) , resp [ 1 ] . UserID )
2023-01-26 07:46:30 -06:00
assert . Equal ( t , dashboards . PERMISSION_ADMIN , resp [ 1 ] . Permission )
2022-02-03 02:20:20 -06:00
} , mockSQLStore )
2020-11-24 05:10:32 -06:00
2022-07-18 08:14:58 -05:00
cmd := dtos . UpdateDashboardACLCommand {
Items : [ ] dtos . DashboardACLUpdateItem {
2023-01-26 07:46:30 -06:00
{ UserID : 1000 , Permission : dashboards . PERMISSION_ADMIN } ,
2020-11-24 05:10:32 -06:00
} ,
}
for _ , acl := range resp {
2022-07-18 08:14:58 -05:00
cmd . Items = append ( cmd . Items , dtos . DashboardACLUpdateItem {
2023-01-20 07:58:47 -06:00
UserID : acl . UserID ,
2020-11-24 05:10:32 -06:00
Permission : acl . Permission ,
} )
}
assert . Len ( t , cmd . Items , 3 )
updateFolderPermissionScenario ( t , updatePermissionContext {
desc : "When calling POST on" ,
url : "/api/folders/uid/permissions" ,
routePattern : "/api/folders/:uid/permissions" ,
cmd : cmd ,
fn : func ( sc * scenarioContext ) {
sc . fakeReqWithParams ( "POST" , sc . url , map [ string ] string { } ) . exec ( )
assert . Equal ( t , 200 , sc . resp . Code )
2021-03-17 10:06:10 -05:00
assert . Len ( t , gotItems , 4 )
2020-11-24 05:10:32 -06:00
} ,
} , hs )
2018-02-20 11:11:50 -06:00
} )
}
2020-11-24 05:10:32 -06:00
func callGetFolderPermissions ( sc * scenarioContext , hs * HTTPServer ) {
sc . handlerFunc = hs . GetFolderPermissionList
2018-02-20 11:11:50 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
}
2021-03-17 10:06:10 -05:00
func callUpdateFolderPermissions ( t * testing . T , sc * scenarioContext ) {
t . Helper ( )
2018-02-20 11:11:50 -06:00
sc . fakeReqWithParams ( "POST" , sc . url , map [ string ] string { } ) . exec ( )
}
2020-11-24 05:10:32 -06:00
func updateFolderPermissionScenario ( t * testing . T , ctx updatePermissionContext , hs * HTTPServer ) {
t . Run ( fmt . Sprintf ( "%s %s" , ctx . desc , ctx . url ) , func ( t * testing . T ) {
sc := setupScenarioContext ( t , ctx . url )
2018-02-20 11:11:50 -06:00
2023-01-27 01:50:36 -06:00
sc . defaultHandler = routing . Wrap ( func ( c * contextmodel . ReqContext ) response . Response {
2021-11-29 03:18:01 -06:00
c . Req . Body = mockRequestBody ( ctx . cmd )
2022-02-09 06:44:38 -06:00
c . Req . Header . Add ( "Content-Type" , "application/json" )
2018-02-20 11:11:50 -06:00
sc . context = c
2022-08-11 06:28:55 -05:00
sc . context . OrgID = testOrgID
sc . context . UserID = testUserID
2018-02-20 11:11:50 -06:00
2021-11-29 03:18:01 -06:00
return hs . UpdateFolderPermissions ( c )
2018-02-20 11:11:50 -06:00
} )
2020-11-24 05:10:32 -06:00
sc . m . Post ( ctx . routePattern , sc . defaultHandler )
2018-02-20 11:11:50 -06:00
2020-11-24 05:10:32 -06:00
ctx . fn ( sc )
2018-02-20 11:11:50 -06:00
} )
}