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"
2020-03-04 05:57:20 -06:00
"github.com/grafana/grafana/pkg/models"
2022-05-10 08:48:47 -05:00
accesscontrolmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
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"
2018-02-20 11:11:50 -06:00
"github.com/grafana/grafana/pkg/services/guardian"
2022-02-03 02:20:20 -06:00
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
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-02-16 07:15:44 -06:00
folderService := & dashboards . FakeFolderService { }
defer folderService . AssertExpectations ( t )
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-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 ,
2022-03-10 05:58:18 -06:00
dashboardService : service . ProvideDashboardService (
2022-05-10 08:48:47 -05:00
settings , dashboardStore , nil , features , folderPermissions , dashboardPermissions ,
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 ) {
folderService . On ( "GetFolderByUID" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( nil , models . ErrFolderNotFound ) . Twice ( )
2022-02-03 02:20:20 -06:00
mockSQLStore := mockstore . NewSQLStoreMock ( )
2020-11-13 02:52:38 -06:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , models . ROLE_EDITOR , 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
cmd := dtos . UpdateDashboardAclCommand {
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-17 10:09:14 -06:00
{ UserID : 1000 , Permission : models . 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
} )
guardian . MockDashboardGuardian ( & guardian . FakeDashboardGuardian { CanAdminValue : false } )
2022-02-16 07:15:44 -06:00
folderService . On ( "GetFolderByUID" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( nil , models . ErrFolderAccessDenied ) . Twice ( )
2022-02-03 02:20:20 -06:00
mockSQLStore := mockstore . NewSQLStoreMock ( )
2022-02-16 07:15:44 -06:00
2020-11-13 02:52:38 -06:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , models . ROLE_EDITOR , 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
cmd := dtos . UpdateDashboardAclCommand {
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-17 10:09:14 -06:00
{ UserID : 1000 , Permission : models . 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 ,
GetAclValue : [ ] * models . DashboardAclInfoDTO {
{ OrgId : 1 , DashboardId : 1 , UserId : 2 , Permission : models . PERMISSION_VIEW } ,
{ OrgId : 1 , DashboardId : 1 , UserId : 3 , Permission : models . PERMISSION_EDIT } ,
{ OrgId : 1 , DashboardId : 1 , UserId : 4 , Permission : models . PERMISSION_ADMIN } ,
{ OrgId : 1 , DashboardId : 1 , TeamId : 1 , Permission : models . PERMISSION_VIEW } ,
{ OrgId : 1 , DashboardId : 1 , TeamId : 2 , Permission : models . PERMISSION_ADMIN } ,
} ,
} )
2022-02-16 07:15:44 -06:00
folderResponse := & models . Folder { Id : 1 , Uid : "uid" , Title : "Folder" }
folderService . On ( "GetFolderByUID" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( folderResponse , nil ) . Twice ( )
dashboardStore . On ( "UpdateDashboardACL" , mock . Anything , mock . Anything , mock . Anything ) . Return ( nil ) . Once ( )
2022-02-03 02:20:20 -06:00
mockSQLStore := mockstore . NewSQLStoreMock ( )
2022-02-16 07:15:44 -06:00
2020-11-13 02:52:38 -06:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , models . ROLE_ADMIN , 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
var resp [ ] * models . DashboardAclInfoDTO
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 )
assert . Equal ( t , int64 ( 2 ) , resp [ 0 ] . UserId )
assert . Equal ( t , models . PERMISSION_VIEW , resp [ 0 ] . Permission )
2022-02-03 02:20:20 -06:00
} , mockSQLStore )
2020-11-13 02:52:38 -06:00
cmd := dtos . UpdateDashboardAclCommand {
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-17 10:09:14 -06:00
{ UserID : 1000 , Permission : models . 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-02-16 07:15:44 -06:00
folderResponse := & models . Folder { Id : 1 , Uid : "uid" , Title : "Folder" }
folderService . On ( "GetFolderByUID" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( folderResponse , nil ) . Once ( )
2020-11-13 02:52:38 -06:00
cmd := dtos . UpdateDashboardAclCommand {
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-17 10:09:14 -06:00
{ UserID : 1000 , Permission : models . 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 ) {
role := models . ROLE_ADMIN
cmds := [ ] dtos . UpdateDashboardAclCommand {
{
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-18 09:16:58 -06:00
{ UserID : 1000 , Permission : models . PERMISSION_ADMIN , Role : & role } ,
2020-11-18 08:36:41 -06:00
} ,
} ,
{
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-18 09:16:58 -06:00
{ TeamID : 1000 , Permission : models . 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 )
assert . Equal ( t , models . 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-02-16 07:15:44 -06:00
folderResponse := & models . Folder { Id : 1 , Uid : "uid" , Title : "Folder" }
folderService . On ( "GetFolderByUID" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( folderResponse , nil ) . Once ( )
2020-11-13 02:52:38 -06:00
cmd := dtos . UpdateDashboardAclCommand {
Items : [ ] dtos . DashboardAclUpdateItem {
2020-11-17 10:09:14 -06:00
{ UserID : 1000 , Permission : models . 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 ,
GetAclValue : [ ] * models . DashboardAclInfoDTO {
{ OrgId : 1 , DashboardId : 1 , UserId : 2 , UserLogin : "hiddenUser" , Permission : models . PERMISSION_VIEW } ,
{ OrgId : 1 , DashboardId : 1 , UserId : 3 , UserLogin : testUserLogin , Permission : models . PERMISSION_EDIT } ,
{ OrgId : 1 , DashboardId : 1 , UserId : 4 , UserLogin : "user_1" , Permission : models . PERMISSION_ADMIN } ,
} ,
GetHiddenAclValue : [ ] * models . DashboardAcl {
{ OrgID : 1 , DashboardID : 1 , UserID : 2 , Permission : models . PERMISSION_VIEW } ,
} ,
} )
2022-02-16 07:15:44 -06:00
var gotItems [ ] * models . DashboardAcl
2020-11-24 05:10:32 -06:00
2022-02-16 07:15:44 -06:00
folderResponse := & models . Folder { Id : 1 , Uid : "uid" , Title : "Folder" }
folderService . On ( "GetFolderByUID" , mock . Anything , mock . Anything , mock . Anything , mock . Anything ) . Return ( folderResponse , nil ) . Twice ( )
dashboardStore . On ( "UpdateDashboardACL" , mock . Anything , mock . Anything , mock . Anything ) . Run ( func ( args mock . Arguments ) {
gotItems = args . Get ( 2 ) . ( [ ] * models . DashboardAcl )
} ) . Return ( nil ) . Once ( )
2020-11-24 05:10:32 -06:00
var resp [ ] * models . DashboardAclInfoDTO
2022-02-03 02:20:20 -06:00
mockSQLStore := mockstore . NewSQLStoreMock ( )
2020-11-24 05:10:32 -06:00
loggedInUserScenarioWithRole ( t , "When calling GET on" , "GET" , "/api/folders/uid/permissions" , "/api/folders/:uid/permissions" , models . ROLE_ADMIN , func ( sc * scenarioContext ) {
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 )
assert . Equal ( t , int64 ( 3 ) , resp [ 0 ] . UserId )
assert . Equal ( t , models . PERMISSION_EDIT , resp [ 0 ] . Permission )
assert . Equal ( t , int64 ( 4 ) , resp [ 1 ] . UserId )
assert . Equal ( t , models . PERMISSION_ADMIN , resp [ 1 ] . Permission )
2022-02-03 02:20:20 -06:00
} , mockSQLStore )
2020-11-24 05:10:32 -06:00
cmd := dtos . UpdateDashboardAclCommand {
Items : [ ] dtos . DashboardAclUpdateItem {
{ UserID : 1000 , Permission : models . PERMISSION_ADMIN } ,
} ,
}
for _ , acl := range resp {
cmd . Items = append ( cmd . Items , dtos . DashboardAclUpdateItem {
UserID : acl . UserId ,
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
2021-01-15 07:43:20 -06:00
sc . defaultHandler = routing . Wrap ( func ( c * models . 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
2020-11-13 02:52:38 -06: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
} )
}