2017-06-12 21:48:55 +08:00
package api
import (
"encoding/json"
2017-06-13 05:05:32 +08:00
"path/filepath"
2017-06-12 21:48:55 +08:00
"testing"
2017-06-13 05:05:32 +08:00
macaron "gopkg.in/macaron.v1"
"github.com/go-macaron/session"
2017-06-12 21:48:55 +08:00
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
2017-06-13 05:05:32 +08:00
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
2017-06-12 21:48:55 +08:00
"github.com/grafana/grafana/pkg/models"
2017-06-13 05:05:32 +08:00
"github.com/grafana/grafana/pkg/services/alerting"
2017-06-12 21:48:55 +08:00
. "github.com/smartystreets/goconvey/convey"
)
func TestDashboardApiEndpoint ( t * testing . T ) {
Convey ( "Given a dashboard with a parent folder which does not have an acl" , t , func ( ) {
fakeDash := models . NewDashboard ( "Child dash" )
2017-06-18 06:24:38 +08:00
fakeDash . Id = 1
2017-06-12 21:48:55 +08:00
fakeDash . ParentId = 1
fakeDash . HasAcl = false
bus . AddHandler ( "test" , func ( query * models . GetDashboardQuery ) error {
query . Result = fakeDash
return nil
} )
2017-06-13 05:05:32 +08:00
cmd := models . SaveDashboardCommand {
Dashboard : simplejson . NewFromAny ( map [ string ] interface { } {
"parentId" : fakeDash . ParentId ,
"title" : fakeDash . Title ,
2017-06-18 06:24:38 +08:00
"id" : fakeDash . Id ,
2017-06-13 05:05:32 +08:00
} ) ,
}
2017-06-12 21:48:55 +08:00
Convey ( "When user is an Org Viewer" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_VIEWER
2017-06-12 21:48:55 +08:00
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
2017-06-12 21:48:55 +08:00
Convey ( "Should not be able to edit or save dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeFalse )
So ( dash . Meta . CanSave , ShouldBeFalse )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-12 21:48:55 +08:00
} )
Convey ( "When user is an Org Read Only Editor" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_READ_ONLY_EDITOR
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
2017-06-12 21:48:55 +08:00
Convey ( "Should be able to edit but not save the dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeTrue )
So ( dash . Meta . CanSave , ShouldBeFalse )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-12 21:48:55 +08:00
} )
Convey ( "When user is an Org Editor" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_EDITOR
2017-06-12 21:48:55 +08:00
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
2017-06-12 21:48:55 +08:00
Convey ( "Should be able to edit or save dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeTrue )
So ( dash . Meta . CanSave , ShouldBeTrue )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
2017-06-16 08:45:21 +08:00
Convey ( "When saving a dashboard folder in another folder" , func ( ) {
bus . AddHandler ( "test" , func ( query * models . GetDashboardQuery ) error {
query . Result = fakeDash
query . Result . IsFolder = true
return nil
} )
invalidCmd := models . SaveDashboardCommand {
2017-06-18 06:24:38 +08:00
ParentId : fakeDash . ParentId ,
IsFolder : true ,
2017-06-16 08:45:21 +08:00
Dashboard : simplejson . NewFromAny ( map [ string ] interface { } {
"parentId" : fakeDash . ParentId ,
"title" : fakeDash . Title ,
} ) ,
}
Convey ( "Should return an error" , func ( ) {
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , invalidCmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 400 )
} )
} )
} )
2017-06-12 21:48:55 +08:00
} )
} )
Convey ( "Given a dashboard with a parent folder which has an acl" , t , func ( ) {
fakeDash := models . NewDashboard ( "Child dash" )
2017-06-18 06:24:38 +08:00
fakeDash . Id = 1
2017-06-12 21:48:55 +08:00
fakeDash . ParentId = 1
fakeDash . HasAcl = true
bus . AddHandler ( "test" , func ( query * models . GetDashboardQuery ) error {
query . Result = fakeDash
return nil
} )
bus . AddHandler ( "test" , func ( query * models . GetUserGroupsByUserQuery ) error {
query . Result = [ ] * models . UserGroup { }
return nil
} )
2017-06-13 05:05:32 +08:00
cmd := models . SaveDashboardCommand {
ParentId : fakeDash . ParentId ,
Dashboard : simplejson . NewFromAny ( map [ string ] interface { } {
2017-06-18 06:24:38 +08:00
"id" : fakeDash . Id ,
2017-06-13 05:05:32 +08:00
"parentId" : fakeDash . ParentId ,
"title" : fakeDash . Title ,
} ) ,
}
2017-06-12 21:48:55 +08:00
Convey ( "When user is an Org Viewer and has no permissions for this dashboard" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_VIEWER
2017-06-12 21:48:55 +08:00
bus . AddHandler ( "test" , func ( query * models . GetDashboardPermissionsQuery ) error {
query . Result = [ ] * models . DashboardAclInfoDTO { }
return nil
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
2017-06-12 21:48:55 +08:00
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
Convey ( "Should be denied access" , func ( ) {
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-12 21:48:55 +08:00
} )
Convey ( "When user is an Org Editor and has no permissions for this dashboard" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_EDITOR
2017-06-12 21:48:55 +08:00
bus . AddHandler ( "test" , func ( query * models . GetDashboardPermissionsQuery ) error {
query . Result = [ ] * models . DashboardAclInfoDTO { }
return nil
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
2017-06-12 21:48:55 +08:00
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
Convey ( "Should be denied access" , func ( ) {
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-12 21:48:55 +08:00
} )
Convey ( "When user is an Org Viewer but has an edit permission" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_VIEWER
2017-06-12 21:48:55 +08:00
mockResult := [ ] * models . DashboardAclInfoDTO {
2017-06-18 06:24:38 +08:00
{ Id : 1 , OrgId : 1 , DashboardId : 2 , UserId : 1 , Permissions : models . PERMISSION_EDIT } ,
2017-06-12 21:48:55 +08:00
}
bus . AddHandler ( "test" , func ( query * models . GetDashboardPermissionsQuery ) error {
query . Result = mockResult
return nil
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
2017-06-12 21:48:55 +08:00
Convey ( "Should be able to get dashboard with edit rights" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeTrue )
So ( dash . Meta . CanSave , ShouldBeTrue )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
2017-06-12 21:48:55 +08:00
} )
Convey ( "When user is an Org Editor but has a view permission" , func ( ) {
2017-06-13 05:05:32 +08:00
role := models . ROLE_EDITOR
2017-06-12 21:48:55 +08:00
mockResult := [ ] * models . DashboardAclInfoDTO {
2017-06-18 06:24:38 +08:00
{ Id : 1 , OrgId : 1 , DashboardId : 2 , UserId : 1 , Permissions : models . PERMISSION_VIEW } ,
2017-06-12 21:48:55 +08:00
}
bus . AddHandler ( "test" , func ( query * models . GetDashboardPermissionsQuery ) error {
query . Result = mockResult
return nil
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
2017-06-12 21:48:55 +08:00
Convey ( "Should not be able to edit or save dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeFalse )
So ( dash . Meta . CanSave , ShouldBeFalse )
} )
} )
2017-06-13 05:05:32 +08:00
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-14 06:28:34 +08:00
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions/1" , "/api/dashboards/id/:dashboardId/versions/:id" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersion ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/id/2/versions" , "/api/dashboards/id/:dashboardId/versions" , role , func ( sc * scenarioContext ) {
CallGetDashboardVersions ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-13 05:05:32 +08:00
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
2017-06-12 21:48:55 +08:00
} )
} )
}
2017-06-13 05:05:32 +08:00
func GetDashboardShouldReturn200 ( sc * scenarioContext ) dtos . DashboardFullWithMeta {
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
So ( sc . resp . Code , ShouldEqual , 200 )
dash := dtos . DashboardFullWithMeta { }
err := json . NewDecoder ( sc . resp . Body ) . Decode ( & dash )
So ( err , ShouldBeNil )
return dash
}
2017-06-14 06:28:34 +08:00
func CallGetDashboardVersion ( sc * scenarioContext ) {
bus . AddHandler ( "test" , func ( query * models . GetDashboardVersionQuery ) error {
query . Result = & models . DashboardVersion { }
return nil
} )
sc . handlerFunc = GetDashboardVersion
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
}
func CallGetDashboardVersions ( sc * scenarioContext ) {
bus . AddHandler ( "test" , func ( query * models . GetDashboardVersionsQuery ) error {
query . Result = [ ] * models . DashboardVersionDTO { }
return nil
} )
sc . handlerFunc = GetDashboardVersions
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
}
2017-06-13 05:05:32 +08:00
func CallDeleteDashboard ( sc * scenarioContext ) {
bus . AddHandler ( "test" , func ( cmd * models . DeleteDashboardCommand ) error {
return nil
} )
sc . handlerFunc = DeleteDashboard
sc . fakeReqWithParams ( "DELETE" , sc . url , map [ string ] string { } ) . exec ( )
}
func CallPostDashboard ( sc * scenarioContext ) {
bus . AddHandler ( "test" , func ( cmd * alerting . ValidateDashboardAlertsCommand ) error {
return nil
} )
bus . AddHandler ( "test" , func ( cmd * models . SaveDashboardCommand ) error {
cmd . Result = & models . Dashboard { Id : 2 , Slug : "Dash" , Version : 2 }
return nil
} )
bus . AddHandler ( "test" , func ( cmd * alerting . UpdateDashboardAlertsCommand ) error {
return nil
} )
sc . fakeReqWithParams ( "POST" , sc . url , map [ string ] string { } ) . exec ( )
}
func postDashboardScenario ( desc string , url string , routePattern string , role models . RoleType , cmd models . SaveDashboardCommand , fn scenarioFunc ) {
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
sc := & scenarioContext {
url : url ,
}
viewsPath , _ := filepath . Abs ( "../../public/views" )
sc . m = macaron . New ( )
sc . m . Use ( macaron . Renderer ( macaron . RenderOptions {
Directory : viewsPath ,
Delims : macaron . Delims { Left : "[[" , Right : "]]" } ,
} ) )
sc . m . Use ( middleware . GetContextHandler ( ) )
sc . m . Use ( middleware . Sessioner ( & session . Options { } ) )
sc . defaultHandler = wrap ( func ( c * middleware . Context ) Response {
sc . context = c
sc . context . UserId = TestUserID
sc . context . OrgId = TestOrgID
sc . context . OrgRole = role
return PostDashboard ( c , cmd )
} )
sc . m . Post ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}