@ -2,11 +2,18 @@ package api
import (
"encoding/json"
"path/filepath"
"testing"
macaron "gopkg.in/macaron.v1"
"github.com/go-macaron/session"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
)
@ -22,56 +29,79 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil
} )
cmd := models . SaveDashboardCommand {
Dashboard : simplejson . NewFromAny ( map [ string ] interface { } {
"parentId" : fakeDash . ParentId ,
"title" : fakeDash . Title ,
} ) ,
}
Convey ( "When user is an Org Viewer" , func ( ) {
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_VIEWER , func ( sc * scenarioContext ) {
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
So ( sc . resp . Code , ShouldEqual , 200 )
role := models . ROLE_VIEWER
dash := dtos . DashboardFullWithMeta { }
err := json . NewDecoder ( sc . resp . Body ) . Decode ( & dash )
So ( err , ShouldBeNil )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
Convey ( "Should not be able to edit or save dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeFalse )
So ( dash . Meta . CanSave , ShouldBeFalse )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
Convey ( "When user is an Org Read Only Editor" , func ( ) {
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_READ_ONLY_EDITOR , func ( sc * scenarioContext ) {
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 )
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 )
Convey ( "Should be able to edit but not save the dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeTrue )
So ( dash . Meta . CanSave , ShouldBeFalse )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
Convey ( "When user is an Org Editor" , func ( ) {
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_EDITOR , func ( sc * scenarioContext ) {
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
role := models . ROLE_EDITOR
So ( sc . resp . Code , ShouldEqual , 200 )
dash := dtos . DashboardFullWithMeta { }
err := json . NewDecoder ( sc . resp . Body ) . Decode ( & dash )
So ( err , ShouldBeNil )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
Convey ( "Should be able to edit or save dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeTrue )
So ( dash . Meta . CanSave , ShouldBeTrue )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
} )
} )
@ -90,13 +120,22 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil
} )
cmd := models . SaveDashboardCommand {
ParentId : fakeDash . ParentId ,
Dashboard : simplejson . NewFromAny ( map [ string ] interface { } {
"parentId" : fakeDash . ParentId ,
"title" : fakeDash . Title ,
} ) ,
}
Convey ( "When user is an Org Viewer and has no permissions for this dashboard" , func ( ) {
role := models . ROLE_VIEWER
bus . AddHandler ( "test" , func ( query * models . GetDashboardPermissionsQuery ) error {
query . Result = [ ] * models . DashboardAclInfoDTO { }
return nil
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_VIEWER , func ( sc * scenarioContext ) {
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
@ -104,15 +143,27 @@ func TestDashboardApiEndpoint(t *testing.T) {
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
Convey ( "When user is an Org Editor and has no permissions for this dashboard" , func ( ) {
role := models . ROLE_EDITOR
bus . AddHandler ( "test" , func ( query * models . GetDashboardPermissionsQuery ) error {
query . Result = [ ] * models . DashboardAclInfoDTO { }
return nil
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_EDITOR , func ( sc * scenarioContext ) {
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
sc . handlerFunc = GetDashboard
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
@ -120,9 +171,21 @@ func TestDashboardApiEndpoint(t *testing.T) {
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
Convey ( "When user is an Org Viewer but has an edit permission" , func ( ) {
role := models . ROLE_VIEWER
mockResult := [ ] * models . DashboardAclInfoDTO {
{ Id : 1 , OrgId : 1 , DashboardId : 2 , UserId : 1 , PermissionType : models . PERMISSION_EDIT } ,
}
@ -132,24 +195,29 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_VIEWER , func ( sc * scenarioContext ) {
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 )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
Convey ( "Should be able to get dashboard with edit rights" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeTrue )
So ( dash . Meta . CanSave , ShouldBeTrue )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 200 )
} )
} )
Convey ( "When user is an Org Editor but has a view permission" , func ( ) {
role := models . ROLE_EDITOR
mockResult := [ ] * models . DashboardAclInfoDTO {
{ Id : 1 , OrgId : 1 , DashboardId : 2 , UserId : 1 , PermissionType : models . PERMISSION_VIEW } ,
}
@ -159,21 +227,96 @@ func TestDashboardApiEndpoint(t *testing.T) {
return nil
} )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , models . ROLE_VIEWER , func ( sc * scenarioContext ) {
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 )
loggedInUserScenarioWithRole ( "When calling GET on" , "GET" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
dash := GetDashboardShouldReturn200 ( sc )
Convey ( "Should not be able to edit or save dashboard" , func ( ) {
So ( dash . Meta . CanEdit , ShouldBeFalse )
So ( dash . Meta . CanSave , ShouldBeFalse )
} )
} )
loggedInUserScenarioWithRole ( "When calling DELETE on" , "DELETE" , "/api/dashboards/2" , "/api/dashboards/:id" , role , func ( sc * scenarioContext ) {
CallDeleteDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
postDashboardScenario ( "When calling POST on" , "/api/dashboards" , "/api/dashboards" , role , cmd , func ( sc * scenarioContext ) {
CallPostDashboard ( sc )
So ( sc . resp . Code , ShouldEqual , 403 )
} )
} )
} )
}
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
}
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 )
} )
}