Chore: Convert API tests to standard Go lib (#29009)

* Chore: Convert tests to standard Go lib

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>

Co-authored-by: Will Browne <wbrowne@users.noreply.github.com>
pull/29101/head
Arve Knudsen 5 years ago committed by GitHub
parent df8f63de7f
commit cb62e69997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 507
      pkg/api/admin_users_test.go
  2. 142
      pkg/api/alerting_test.go
  3. 2
      pkg/api/annotations.go
  4. 256
      pkg/api/annotations_test.go
  5. 26
      pkg/api/common_test.go
  6. 4
      pkg/api/dashboard.go
  7. 203
      pkg/api/dashboard_permission_test.go
  8. 2
      pkg/api/dashboard_snapshot.go
  9. 329
      pkg/api/dashboard_snapshot_test.go
  10. 1234
      pkg/api/dashboard_test.go
  11. 89
      pkg/api/datasources_test.go
  12. 400
      pkg/api/folder_permission_test.go
  13. 213
      pkg/api/folder_test.go
  14. 2
      pkg/api/frontend_logging_test.go
  15. 1
      pkg/api/ldap_debug.go
  16. 203
      pkg/api/ldap_debug_test.go
  17. 8
      pkg/api/login.go
  18. 36
      pkg/api/login_test.go
  19. 2
      pkg/api/pluginproxy/ds_auth_provider.go
  20. 964
      pkg/api/pluginproxy/ds_proxy_test.go
  21. 74
      pkg/api/team_test.go
  22. 264
      pkg/api/user_test.go
  23. 230
      pkg/api/user_token_test.go
  24. 2
      pkg/services/licensing/oss.go
  25. 2
      pkg/services/rendering/rendering.go
  26. 8
      pkg/services/rendering/rendering_test.go
  27. 14
      pkg/setting/setting.go

@ -1,6 +1,7 @@
package api
import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
@ -8,288 +9,356 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
const (
TestLogin = "test@example.com"
TestPassword = "password"
testLogin = "test@example.com"
testPassword = "password"
nonExistingOrgID = 1000
)
func TestAdminApiEndpoint(t *testing.T) {
role := models.ROLE_ADMIN
Convey("Given a server admin attempts to remove themself as an admin", t, func() {
func TestAdminAPIEndpoint(t *testing.T) {
const role = models.ROLE_ADMIN
t.Run("Given a server admin attempts to remove themself as an admin", func(t *testing.T) {
updateCmd := dtos.AdminUpdateUserPermissionsForm{
IsGrafanaAdmin: false,
}
bus.AddHandler("test", func(cmd *models.UpdateUserPermissionsCommand) error {
return models.ErrLastGrafanaAdmin
})
putAdminScenario(t, "When calling PUT on", "/api/admin/users/1/permissions",
"/api/admin/users/:id/permissions", role, updateCmd, func(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.UpdateUserPermissionsCommand) error {
return models.ErrLastGrafanaAdmin
})
putAdminScenario("When calling PUT on", "/api/admin/users/1/permissions", "/api/admin/users/:id/permissions", role, updateCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 400)
})
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
assert.Equal(t, 400, sc.resp.Code)
})
})
Convey("When a server admin attempts to logout himself from all devices", t, func() {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: TestUserID}
return nil
})
t.Run("When a server admin attempts to logout himself from all devices", func(t *testing.T) {
adminLogoutUserScenario(t, "Should not be allowed when calling POST on",
"/api/admin/users/1/logout", "/api/admin/users/:id/logout", func(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: testUserID}
return nil
})
adminLogoutUserScenario("Should not be allowed when calling POST on", "/api/admin/users/1/logout", "/api/admin/users/:id/logout", func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 400)
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 400, sc.resp.Code)
})
})
Convey("When a server admin attempts to logout a non-existing user from all devices", t, func() {
userId := int64(0)
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userId = cmd.Id
return models.ErrUserNotFound
})
t.Run("When a server admin attempts to logout a non-existing user from all devices", func(t *testing.T) {
adminLogoutUserScenario(t, "Should return not found when calling POST on", "/api/admin/users/200/logout",
"/api/admin/users/:id/logout", func(sc *scenarioContext) {
userID := int64(0)
adminLogoutUserScenario("Should return not found when calling POST on", "/api/admin/users/200/logout", "/api/admin/users/:id/logout", func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
So(userId, ShouldEqual, 200)
})
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userID = cmd.Id
return models.ErrUserNotFound
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
assert.Equal(t, int64(200), userID)
})
})
Convey("When a server admin attempts to revoke an auth token for a non-existing user", t, func() {
userId := int64(0)
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userId = cmd.Id
return models.ErrUserNotFound
})
t.Run("When a server admin attempts to revoke an auth token for a non-existing user", func(t *testing.T) {
cmd := models.RevokeAuthTokenCmd{AuthTokenId: 2}
adminRevokeUserAuthTokenScenario("Should return not found when calling POST on", "/api/admin/users/200/revoke-auth-token", "/api/admin/users/:id/revoke-auth-token", cmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
So(userId, ShouldEqual, 200)
})
})
adminRevokeUserAuthTokenScenario(t, "Should return not found when calling POST on",
"/api/admin/users/200/revoke-auth-token", "/api/admin/users/:id/revoke-auth-token", cmd, func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userID = cmd.Id
return models.ErrUserNotFound
})
Convey("When a server admin gets auth tokens for a non-existing user", t, func() {
userId := int64(0)
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userId = cmd.Id
return models.ErrUserNotFound
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
assert.Equal(t, int64(200), userID)
})
})
adminGetUserAuthTokensScenario("Should return not found when calling GET on", "/api/admin/users/200/auth-tokens", "/api/admin/users/:id/auth-tokens", func(sc *scenarioContext) {
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
So(userId, ShouldEqual, 200)
})
t.Run("When a server admin gets auth tokens for a non-existing user", func(t *testing.T) {
adminGetUserAuthTokensScenario(t, "Should return not found when calling GET on",
"/api/admin/users/200/auth-tokens", "/api/admin/users/:id/auth-tokens", func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userID = cmd.Id
return models.ErrUserNotFound
})
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
assert.Equal(t, int64(200), userID)
})
})
Convey("When a server admin attempts to enable/disable a nonexistent user", t, func() {
var userId int64
isDisabled := false
bus.AddHandler("test", func(cmd *models.GetAuthInfoQuery) error {
return models.ErrUserNotFound
})
t.Run("When a server admin attempts to enable/disable a nonexistent user", func(t *testing.T) {
adminDisableUserScenario(t, "Should return user not found on a POST request", "enable",
"/api/admin/users/42/enable", "/api/admin/users/:id/enable", func(sc *scenarioContext) {
var userID int64
isDisabled := false
bus.AddHandler("test", func(cmd *models.GetAuthInfoQuery) error {
return models.ErrUserNotFound
})
bus.AddHandler("test", func(cmd *models.DisableUserCommand) error {
userID = cmd.UserId
isDisabled = cmd.IsDisabled
return models.ErrUserNotFound
})
bus.AddHandler("test", func(cmd *models.DisableUserCommand) error {
userId = cmd.UserId
isDisabled = cmd.IsDisabled
return models.ErrUserNotFound
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
adminDisableUserScenario("Should return user not found on a POST request", "enable", "/api/admin/users/42/enable", "/api/admin/users/:id/enable", func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
So(sc.resp.Code, ShouldEqual, 404)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
assert.Equal(t, "user not found", respJSON.Get("message").MustString())
So(respJSON.Get("message").MustString(), ShouldEqual, "user not found")
assert.Equal(t, int64(42), userID)
assert.Equal(t, false, isDisabled)
})
So(userId, ShouldEqual, 42)
So(isDisabled, ShouldEqual, false)
})
adminDisableUserScenario(t, "Should return user not found on a POST request", "disable",
"/api/admin/users/42/disable", "/api/admin/users/:id/disable", func(sc *scenarioContext) {
var userID int64
isDisabled := false
bus.AddHandler("test", func(cmd *models.GetAuthInfoQuery) error {
return models.ErrUserNotFound
})
adminDisableUserScenario("Should return user not found on a POST request", "disable", "/api/admin/users/42/disable", "/api/admin/users/:id/disable", func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
bus.AddHandler("test", func(cmd *models.DisableUserCommand) error {
userID = cmd.UserId
isDisabled = cmd.IsDisabled
return models.ErrUserNotFound
})
So(sc.resp.Code, ShouldEqual, 404)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(respJSON.Get("message").MustString(), ShouldEqual, "user not found")
assert.Equal(t, 404, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
So(userId, ShouldEqual, 42)
So(isDisabled, ShouldEqual, true)
})
assert.Equal(t, "user not found", respJSON.Get("message").MustString())
assert.Equal(t, int64(42), userID)
assert.Equal(t, true, isDisabled)
})
})
Convey("When a server admin attempts to disable/enable external user", t, func() {
userId := int64(0)
bus.AddHandler("test", func(cmd *models.GetAuthInfoQuery) error {
userId = cmd.UserId
return nil
})
t.Run("When a server admin attempts to disable/enable external user", func(t *testing.T) {
adminDisableUserScenario(t, "Should return Could not disable external user error", "disable",
"/api/admin/users/42/disable", "/api/admin/users/:id/disable", func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.GetAuthInfoQuery) error {
userID = cmd.UserId
return nil
})
adminDisableUserScenario("Should return Could not disable external user error", "disable", "/api/admin/users/42/disable", "/api/admin/users/:id/disable", func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 500)
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 500, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("message").MustString(), ShouldEqual, "Could not disable external user")
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, "Could not disable external user", respJSON.Get("message").MustString())
So(userId, ShouldEqual, 42)
})
assert.Equal(t, int64(42), userID)
})
adminDisableUserScenario("Should return Could not enable external user error", "enable", "/api/admin/users/42/enable", "/api/admin/users/:id/enable", func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 500)
adminDisableUserScenario(t, "Should return Could not enable external user error", "enable",
"/api/admin/users/42/enable", "/api/admin/users/:id/enable", func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.GetAuthInfoQuery) error {
userID = cmd.UserId
return nil
})
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("message").MustString(), ShouldEqual, "Could not enable external user")
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 500, sc.resp.Code)
So(userId, ShouldEqual, 42)
})
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, "Could not enable external user", respJSON.Get("message").MustString())
assert.Equal(t, int64(42), userID)
})
})
Convey("When a server admin attempts to delete a nonexistent user", t, func() {
var userId int64
bus.AddHandler("test", func(cmd *models.DeleteUserCommand) error {
userId = cmd.UserId
return models.ErrUserNotFound
})
t.Run("When a server admin attempts to delete a nonexistent user", func(t *testing.T) {
adminDeleteUserScenario(t, "Should return user not found error", "/api/admin/users/42",
"/api/admin/users/:id", func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.DeleteUserCommand) error {
userID = cmd.UserId
return models.ErrUserNotFound
})
adminDeleteUserScenario("Should return user not found error", "/api/admin/users/42", "/api/admin/users/:id", func(sc *scenarioContext) {
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
assert.Equal(t, 404, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("message").MustString(), ShouldEqual, "user not found")
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, "user not found", respJSON.Get("message").MustString())
So(userId, ShouldEqual, 42)
})
assert.Equal(t, int64(42), userID)
})
})
Convey("When a server admin attempts to create a user", t, func() {
var userLogin string
var orgId int64
t.Run("When a server admin attempts to create a user", func(t *testing.T) {
t.Run("Without an organization", func(t *testing.T) {
createCmd := dtos.AdminCreateUserForm{
Login: testLogin,
Password: testPassword,
}
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
userLogin = cmd.Login
orgId = cmd.OrgId
adminCreateUserScenario(t, "Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
bus.ClearBusHandlers()
if orgId == nonExistingOrgID {
return models.ErrOrgNotFound
}
var userLogin string
var orgID int64
cmd.Result = models.User{Id: TestUserID}
return nil
})
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
userLogin = cmd.Login
orgID = cmd.OrgId
Convey("Without an organization", func() {
createCmd := dtos.AdminCreateUserForm{
Login: TestLogin,
Password: TestPassword,
}
if orgID == nonExistingOrgID {
return models.ErrOrgNotFound
}
cmd.Result = models.User{Id: testUserID}
return nil
})
adminCreateUserScenario("Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("id").MustInt64(), ShouldEqual, TestUserID)
So(respJSON.Get("message").MustString(), ShouldEqual, "User created")
require.NoError(t, err)
assert.Equal(t, testUserID, respJSON.Get("id").MustInt64())
assert.Equal(t, "User created", respJSON.Get("message").MustString())
// test that userLogin and orgId were transmitted correctly to the handler
So(userLogin, ShouldEqual, TestLogin)
So(orgId, ShouldEqual, 0)
// Verify that userLogin and orgID were transmitted correctly to the handler
assert.Equal(t, testLogin, userLogin)
assert.Equal(t, int64(0), orgID)
})
})
Convey("With an organization", func() {
t.Run("With an organization", func(t *testing.T) {
createCmd := dtos.AdminCreateUserForm{
Login: TestLogin,
Password: TestPassword,
OrgId: TestOrgID,
Login: testLogin,
Password: testPassword,
OrgId: testOrgID,
}
adminCreateUserScenario("Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
adminCreateUserScenario(t, "Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
bus.ClearBusHandlers()
var userLogin string
var orgID int64
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
userLogin = cmd.Login
orgID = cmd.OrgId
if orgID == nonExistingOrgID {
return models.ErrOrgNotFound
}
cmd.Result = models.User{Id: testUserID}
return nil
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("id").MustInt64(), ShouldEqual, TestUserID)
So(respJSON.Get("message").MustString(), ShouldEqual, "User created")
require.NoError(t, err)
assert.Equal(t, testUserID, respJSON.Get("id").MustInt64())
assert.Equal(t, "User created", respJSON.Get("message").MustString())
So(userLogin, ShouldEqual, TestLogin)
So(orgId, ShouldEqual, TestOrgID)
assert.Equal(t, testLogin, userLogin)
assert.Equal(t, testOrgID, orgID)
})
})
Convey("With a nonexistent organization", func() {
t.Run("With a nonexistent organization", func(t *testing.T) {
createCmd := dtos.AdminCreateUserForm{
Login: TestLogin,
Password: TestPassword,
Login: testLogin,
Password: testPassword,
OrgId: nonExistingOrgID,
}
adminCreateUserScenario("Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
adminCreateUserScenario(t, "Should create the user", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
bus.ClearBusHandlers()
var userLogin string
var orgID int64
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
userLogin = cmd.Login
orgID = cmd.OrgId
if orgID == nonExistingOrgID {
return models.ErrOrgNotFound
}
cmd.Result = models.User{Id: testUserID}
return nil
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 400)
assert.Equal(t, 400, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("message").MustString(), ShouldEqual, "organization not found")
require.NoError(t, err)
assert.Equal(t, "organization not found", respJSON.Get("message").MustString())
So(userLogin, ShouldEqual, TestLogin)
So(orgId, ShouldEqual, 1000)
assert.Equal(t, testLogin, userLogin)
assert.Equal(t, int64(1000), orgID)
})
})
})
Convey("When a server admin attempts to create a user with an already existing email/login", t, func() {
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
return models.ErrUserAlreadyExists
})
t.Run("When a server admin attempts to create a user with an already existing email/login", func(t *testing.T) {
createCmd := dtos.AdminCreateUserForm{
Login: TestLogin,
Password: TestPassword,
Login: testLogin,
Password: testPassword,
}
adminCreateUserScenario("Should return an error", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
adminCreateUserScenario(t, "Should return an error", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
bus.ClearBusHandlers()
bus.AddHandler("test", func(cmd *models.CreateUserCommand) error {
return models.ErrUserAlreadyExists
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 412)
assert.Equal(t, 412, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("error").MustString(), ShouldEqual, "user already exists")
require.NoError(t, err)
assert.Equal(t, "user already exists", respJSON.Get("error").MustString())
})
})
}
func putAdminScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.AdminUpdateUserPermissionsForm, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func putAdminScenario(t *testing.T, desc string, url string, routePattern string, role models.RoleType,
cmd dtos.AdminUpdateUserPermissionsForm, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return AdminUpdateUserPermissions(c, cmd)
@ -301,20 +370,22 @@ func putAdminScenario(desc string, url string, routePattern string, role models.
})
}
func adminLogoutUserScenario(desc string, url string, routePattern string, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func adminLogoutUserScenario(t *testing.T, desc string, url string, routePattern string, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
hs := HTTPServer{
Bus: bus.GetBus(),
AuthTokenService: auth.NewFakeUserAuthTokenService(),
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
t.Log("Route handler invoked", "url", c.Req.URL)
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.AdminLogoutUser(c)
@ -326,9 +397,9 @@ func adminLogoutUserScenario(desc string, url string, routePattern string, fn sc
})
}
func adminRevokeUserAuthTokenScenario(desc string, url string, routePattern string, cmd models.RevokeAuthTokenCmd, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func adminRevokeUserAuthTokenScenario(t *testing.T, desc string, url string, routePattern string, cmd models.RevokeAuthTokenCmd, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -337,12 +408,12 @@ func adminRevokeUserAuthTokenScenario(desc string, url string, routePattern stri
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.AdminRevokeUserAuthToken(c, cmd)
@ -354,9 +425,9 @@ func adminRevokeUserAuthTokenScenario(desc string, url string, routePattern stri
})
}
func adminGetUserAuthTokensScenario(desc string, url string, routePattern string, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func adminGetUserAuthTokensScenario(t *testing.T, desc string, url string, routePattern string, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -365,12 +436,12 @@ func adminGetUserAuthTokensScenario(desc string, url string, routePattern string
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.AdminGetUserAuthTokens(c)
@ -382,9 +453,9 @@ func adminGetUserAuthTokensScenario(desc string, url string, routePattern string
})
}
func adminDisableUserScenario(desc string, action string, url string, routePattern string, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func adminDisableUserScenario(t *testing.T, desc string, action string, url string, routePattern string, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -393,10 +464,10 @@ func adminDisableUserScenario(desc string, action string, url string, routePatte
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.UserId = testUserID
if action == "enable" {
return AdminEnableUser(c)
@ -411,14 +482,14 @@ func adminDisableUserScenario(desc string, action string, url string, routePatte
})
}
func adminDeleteUserScenario(desc string, url string, routePattern string, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func adminDeleteUserScenario(t *testing.T, desc string, url string, routePattern string, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.UserId = testUserID
return AdminDeleteUser(c)
})
@ -429,14 +500,14 @@ func adminDeleteUserScenario(desc string, url string, routePattern string, fn sc
})
}
func adminCreateUserScenario(desc string, url string, routePattern string, cmd dtos.AdminCreateUserForm, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func adminCreateUserScenario(t *testing.T, desc string, url string, routePattern string, cmd dtos.AdminCreateUserForm, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.UserId = testUserID
return AdminCreateUser(c, cmd)
})

@ -1,29 +1,38 @@
package api
import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/search"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestAlertingApiEndpoint(t *testing.T) {
Convey("Given an alert in a dashboard with an acl", t, func() {
singleAlert := &models.Alert{Id: 1, DashboardId: 1, Name: "singlealert"}
type setUpConf struct {
aclMockResp []*models.DashboardAclInfoDTO
}
func TestAlertingAPIEndpoint(t *testing.T) {
singleAlert := &models.Alert{Id: 1, DashboardId: 1, Name: "singlealert"}
viewerRole := models.ROLE_VIEWER
editorRole := models.ROLE_EDITOR
setUp := func(confs ...setUpConf) {
bus.AddHandler("test", func(query *models.GetAlertByIdQuery) error {
query.Result = singleAlert
return nil
})
viewerRole := models.ROLE_VIEWER
editorRole := models.ROLE_EDITOR
aclMockResp := []*models.DashboardAclInfoDTO{}
for _, c := range confs {
if c.aclMockResp != nil {
aclMockResp = c.aclMockResp
}
}
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
query.Result = aclMockResp
return nil
@ -33,39 +42,45 @@ func TestAlertingApiEndpoint(t *testing.T) {
query.Result = []*models.TeamDTO{}
return nil
})
Convey("When user is editor and not in the ACL", func() {
Convey("Should not be able to pause the alert", func() {
cmd := dtos.PauseAlertCommand{
AlertId: 1,
Paused: true,
}
postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
CallPauseAlert(sc)
So(sc.resp.Code, ShouldEqual, 403)
})
}
t.Run("When user is editor and not in the ACL", func(t *testing.T) {
cmd := dtos.PauseAlertCommand{
AlertId: 1,
Paused: true,
}
postAlertScenario(t, "When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause",
models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
setUp()
callPauseAlert(sc)
assert.Equal(t, 403, sc.resp.Code)
})
})
Convey("When user is editor and dashboard has default ACL", func() {
aclMockResp = []*models.DashboardAclInfoDTO{
{Role: &viewerRole, Permission: models.PERMISSION_VIEW},
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
}
})
Convey("Should be able to pause the alert", func() {
cmd := dtos.PauseAlertCommand{
AlertId: 1,
Paused: true,
}
postAlertScenario("When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause", models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
CallPauseAlert(sc)
So(sc.resp.Code, ShouldEqual, 200)
t.Run("When user is editor and dashboard has default ACL", func(t *testing.T) {
cmd := dtos.PauseAlertCommand{
AlertId: 1,
Paused: true,
}
postAlertScenario(t, "When calling POST on", "/api/alerts/1/pause", "/api/alerts/:alertId/pause",
models.ROLE_EDITOR, cmd, func(sc *scenarioContext) {
setUp(setUpConf{
aclMockResp: []*models.DashboardAclInfoDTO{
{Role: &viewerRole, Permission: models.PERMISSION_VIEW},
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
},
})
callPauseAlert(sc)
assert.Equal(t, 200, sc.resp.Code)
})
})
})
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts",
models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
var searchQuery *search.Query
bus.AddHandler("test", func(query *search.Query) error {
searchQuery = query
@ -81,11 +96,15 @@ func TestAlertingApiEndpoint(t *testing.T) {
sc.handlerFunc = GetAlerts
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(searchQuery, ShouldBeNil)
So(getAlertsQuery, ShouldNotBeNil)
require.Nil(t, searchQuery)
assert.NotNil(t, getAlertsQuery)
})
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1&dashboardId=2&folderId=3&dashboardTag=abc&dashboardQuery=dbQuery&limit=5&query=alertQuery", "/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
loggedInUserScenarioWithRole(t, "When calling GET on", "GET",
"/api/alerts?dashboardId=1&dashboardId=2&folderId=3&dashboardTag=abc&dashboardQuery=dbQuery&limit=5&query=alertQuery",
"/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
var searchQuery *search.Query
bus.AddHandler("test", func(query *search.Query) error {
searchQuery = query
@ -105,29 +124,31 @@ func TestAlertingApiEndpoint(t *testing.T) {
sc.handlerFunc = GetAlerts
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(searchQuery, ShouldNotBeNil)
So(searchQuery.DashboardIds[0], ShouldEqual, 1)
So(searchQuery.DashboardIds[1], ShouldEqual, 2)
So(searchQuery.FolderIds[0], ShouldEqual, 3)
So(searchQuery.Tags[0], ShouldEqual, "abc")
So(searchQuery.Title, ShouldEqual, "dbQuery")
So(getAlertsQuery, ShouldNotBeNil)
So(getAlertsQuery.DashboardIDs[0], ShouldEqual, 1)
So(getAlertsQuery.DashboardIDs[1], ShouldEqual, 2)
So(getAlertsQuery.Limit, ShouldEqual, 5)
So(getAlertsQuery.Query, ShouldEqual, "alertQuery")
require.NotNil(t, searchQuery)
assert.Equal(t, int64(1), searchQuery.DashboardIds[0])
assert.Equal(t, int64(2), searchQuery.DashboardIds[1])
assert.Equal(t, int64(3), searchQuery.FolderIds[0])
assert.Equal(t, "abc", searchQuery.Tags[0])
assert.Equal(t, "dbQuery", searchQuery.Title)
require.NotNil(t, getAlertsQuery)
assert.Equal(t, int64(1), getAlertsQuery.DashboardIDs[0])
assert.Equal(t, int64(2), getAlertsQuery.DashboardIDs[1])
assert.Equal(t, int64(5), getAlertsQuery.Limit)
assert.Equal(t, "alertQuery", getAlertsQuery.Query)
})
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alert-notifications/1", "/alert-notifications/:notificationId", models.ROLE_ADMIN, func(sc *scenarioContext) {
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/alert-notifications/1",
"/alert-notifications/:notificationId", models.ROLE_ADMIN, func(sc *scenarioContext) {
setUp()
sc.handlerFunc = GetAlertNotificationByID
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
assert.Equal(t, 404, sc.resp.Code)
})
})
}
func CallPauseAlert(sc *scenarioContext) {
func callPauseAlert(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.PauseAlertCommand) error {
return nil
})
@ -135,15 +156,16 @@ func CallPauseAlert(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
}
func postAlertScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.PauseAlertCommand, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func postAlertScenario(t *testing.T, desc string, url string, routePattern string, role models.RoleType,
cmd dtos.PauseAlertCommand, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return PauseAlert(c, cmd)

@ -247,7 +247,6 @@ func DeleteAnnotationByID(c *models.ReqContext) Response {
OrgId: c.OrgId,
Id: annotationID,
})
if err != nil {
return Error(500, "Failed to delete annotation", err)
}
@ -272,7 +271,6 @@ func canSaveByDashboardID(c *models.ReqContext, dashboardID int64) (bool, error)
func canSave(c *models.ReqContext, repo annotations.Repository, annotationID int64) Response {
items, err := repo.Find(&annotations.ItemQuery{AnnotationId: annotationID, OrgId: c.OrgId})
if err != nil || len(items) == 0 {
return Error(500, "Could not find annotation to update", err)
}

@ -1,18 +1,18 @@
package api
import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/annotations"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
)
func TestAnnotationsApiEndpoint(t *testing.T) {
Convey("Given an annotation without a dashboard id", t, func() {
func TestAnnotationsAPIEndpoint(t *testing.T) {
t.Run("Given an annotation without a dashboard ID", func(t *testing.T) {
cmd := dtos.PostAnnotationsCmd{
Time: 1000,
Text: "annotation text",
@ -31,60 +31,70 @@ func TestAnnotationsApiEndpoint(t *testing.T) {
Tags: []string{"tag1", "tag2"},
}
Convey("When user is an Org Viewer", func() {
t.Run("When user is an Org Viewer", func(t *testing.T) {
role := models.ROLE_VIEWER
Convey("Should not be allowed to save an annotation", func() {
postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
})
putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
})
patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
})
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
})
t.Run("Should not be allowed to save an annotation", func(t *testing.T) {
postAnnotationScenario(t, "When calling POST on", "/api/annotations", "/api/annotations", role,
cmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
})
putAnnotationScenario(t, "When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId",
role, updateCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
})
patchAnnotationScenario(t, "When calling PATCH on", "/api/annotations/1",
"/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
})
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
fakeAnnoRepo = &fakeAnnotationsRepo{}
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
})
})
})
Convey("When user is an Org Editor", func() {
t.Run("When user is an Org Editor", func(t *testing.T) {
role := models.ROLE_EDITOR
Convey("Should be able to save an annotation", func() {
postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
})
putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
t.Run("Should be able to save an annotation", func(t *testing.T) {
postAnnotationScenario(t, "When calling POST on", "/api/annotations", "/api/annotations", role,
cmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 200, sc.resp.Code)
})
putAnnotationScenario(t, "When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
patchAnnotationScenario(t, "When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
})
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
fakeAnnoRepo = &fakeAnnotationsRepo{}
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 200, sc.resp.Code)
})
})
})
})
Convey("Given an annotation with a dashboard id and the dashboard does not have an acl", t, func() {
t.Run("Given an annotation with a dashboard ID and the dashboard does not have an ACL", func(t *testing.T) {
cmd := dtos.PostAnnotationsCmd{
Time: 1000,
Text: "annotation text",
@ -120,90 +130,111 @@ func TestAnnotationsApiEndpoint(t *testing.T) {
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
}
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
query.Result = aclMockResp
return nil
})
setUp := func() {
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
query.Result = aclMockResp
return nil
})
bus.AddHandler("test", func(query *models.GetTeamsByUserQuery) error {
query.Result = []*models.TeamDTO{}
return nil
})
bus.AddHandler("test", func(query *models.GetTeamsByUserQuery) error {
query.Result = []*models.TeamDTO{}
return nil
})
}
Convey("When user is an Org Viewer", func() {
t.Run("When user is an Org Viewer", func(t *testing.T) {
role := models.ROLE_VIEWER
Convey("Should not be allowed to save an annotation", func() {
postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
t.Run("Should not be allowed to save an annotation", func(t *testing.T) {
postAnnotationScenario(t, "When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
assert.Equal(t, 403, sc.resp.Code)
})
putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
putAnnotationScenario(t, "When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
assert.Equal(t, 403, sc.resp.Code)
})
patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
patchAnnotationScenario(t, "When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
assert.Equal(t, 403, sc.resp.Code)
})
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 403)
})
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
setUp()
fakeAnnoRepo = &fakeAnnotationsRepo{}
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 403, sc.resp.Code)
})
})
})
Convey("When user is an Org Editor", func() {
t.Run("When user is an Org Editor", func(t *testing.T) {
role := models.ROLE_EDITOR
Convey("Should be able to save an annotation", func() {
postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
t.Run("Should be able to save an annotation", func(t *testing.T) {
postAnnotationScenario(t, "When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
putAnnotationScenario(t, "When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
patchAnnotationScenario(t, "When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/annotations/1", "/api/annotations/:annotationId", role, func(sc *scenarioContext) {
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
})
loggedInUserScenarioWithRole(t, "When calling DELETE on", "DELETE", "/api/annotations/1",
"/api/annotations/:annotationId", role, func(sc *scenarioContext) {
setUp()
fakeAnnoRepo = &fakeAnnotationsRepo{}
annotations.SetRepository(fakeAnnoRepo)
sc.handlerFunc = DeleteAnnotationByID
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 200, sc.resp.Code)
})
})
})
Convey("When user is an Admin", func() {
t.Run("When user is an Admin", func(t *testing.T) {
role := models.ROLE_ADMIN
Convey("Should be able to do anything", func() {
postAnnotationScenario("When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
t.Run("Should be able to do anything", func(t *testing.T) {
postAnnotationScenario(t, "When calling POST on", "/api/annotations", "/api/annotations", role, cmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
putAnnotationScenario("When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
putAnnotationScenario(t, "When calling PUT on", "/api/annotations/1", "/api/annotations/:annotationId", role, updateCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
patchAnnotationScenario("When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
patchAnnotationScenario(t, "When calling PATCH on", "/api/annotations/1", "/api/annotations/:annotationId", role, patchCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("PATCH", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
deleteAnnotationsScenario("When calling POST on", "/api/annotations/mass-delete", "/api/annotations/mass-delete", role, deleteCmd, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
})
deleteAnnotationsScenario(t, "When calling POST on", "/api/annotations/mass-delete",
"/api/annotations/mass-delete", role, deleteCmd, func(sc *scenarioContext) {
setUp()
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 200, sc.resp.Code)
})
})
})
})
@ -229,15 +260,16 @@ func (repo *fakeAnnotationsRepo) Find(query *annotations.ItemQuery) ([]*annotati
var fakeAnnoRepo *fakeAnnotationsRepo
func postAnnotationScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.PostAnnotationsCmd, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func postAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role models.RoleType,
cmd dtos.PostAnnotationsCmd, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return PostAnnotation(c, cmd)
@ -252,15 +284,16 @@ func postAnnotationScenario(desc string, url string, routePattern string, role m
})
}
func putAnnotationScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.UpdateAnnotationsCmd, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func putAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role models.RoleType,
cmd dtos.UpdateAnnotationsCmd, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return UpdateAnnotation(c, cmd)
@ -275,15 +308,15 @@ func putAnnotationScenario(desc string, url string, routePattern string, role mo
})
}
func patchAnnotationScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.PatchAnnotationsCmd, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func patchAnnotationScenario(t *testing.T, desc string, url string, routePattern string, role models.RoleType, cmd dtos.PatchAnnotationsCmd, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return PatchAnnotation(c, cmd)
@ -298,15 +331,16 @@ func patchAnnotationScenario(desc string, url string, routePattern string, role
})
}
func deleteAnnotationsScenario(desc string, url string, routePattern string, role models.RoleType, cmd dtos.DeleteAnnotationsCmd, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func deleteAnnotationsScenario(t *testing.T, desc string, url string, routePattern string, role models.RoleType,
cmd dtos.DeleteAnnotationsCmd, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
return DeleteAnnotations(c, cmd)

@ -11,24 +11,23 @@ import (
"github.com/grafana/grafana/pkg/middleware"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/require"
"gopkg.in/macaron.v1"
)
func loggedInUserScenario(desc string, url string, fn scenarioFunc) {
loggedInUserScenarioWithRole(desc, "GET", url, url, models.ROLE_EDITOR, fn)
func loggedInUserScenario(t *testing.T, desc string, url string, fn scenarioFunc) {
loggedInUserScenarioWithRole(t, desc, "GET", url, url, models.ROLE_EDITOR, fn)
}
func loggedInUserScenarioWithRole(desc string, method string, url string, routePattern string, role models.RoleType, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url string, routePattern string, role models.RoleType, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = role
if sc.handlerFunc != nil {
return sc.handlerFunc(sc.context)
@ -48,11 +47,11 @@ func loggedInUserScenarioWithRole(desc string, method string, url string, routeP
})
}
func anonymousUserScenario(desc string, method string, url string, routePattern string, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func anonymousUserScenario(t *testing.T, desc string, method string, url string, routePattern string, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
if sc.handlerFunc != nil {
@ -76,7 +75,7 @@ func anonymousUserScenario(desc string, method string, url string, routePattern
func (sc *scenarioContext) fakeReq(method, url string) *scenarioContext {
sc.resp = httptest.NewRecorder()
req, err := http.NewRequest(method, url, nil)
So(err, ShouldBeNil)
require.NoError(sc.t, err)
sc.req = req
return sc
@ -141,9 +140,10 @@ func (sc *scenarioContext) exec() {
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *models.ReqContext) Response
func setupScenarioContext(url string) *scenarioContext {
func setupScenarioContext(t *testing.T, url string) *scenarioContext {
sc := &scenarioContext{
url: url,
t: t,
}
viewsPath, _ := filepath.Abs("../../public/views")

@ -48,7 +48,9 @@ func dashboardGuardianResponse(err error) Response {
}
func (hs *HTTPServer) GetDashboard(c *models.ReqContext) Response {
dash, rsp := getDashboardHelper(c.OrgId, c.Params(":slug"), 0, c.Params(":uid"))
slug := c.Params(":slug")
uid := c.Params(":uid")
dash, rsp := getDashboardHelper(c.OrgId, slug, 0, uid)
if rsp != nil {
return rsp
}

@ -1,6 +1,7 @@
package api
import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
@ -8,21 +9,25 @@ import (
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/guardian"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestDashboardPermissionApiEndpoint(t *testing.T) {
Convey("Dashboard permissions test", t, func() {
Convey("Given dashboard not exists", func() {
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
return models.ErrDashboardNotFound
})
func TestDashboardPermissionAPIEndpoint(t *testing.T) {
t.Run("Dashboard permissions test", func(t *testing.T) {
t.Run("Given dashboard not exists", func(t *testing.T) {
setUp := func() {
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
return models.ErrDashboardNotFound
})
}
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 404)
})
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
callGetDashboardPermissions(sc)
assert.Equal(t, 404, sc.resp.Code)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -30,26 +35,37 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
},
}
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
callUpdateDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 404)
})
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(sc)
assert.Equal(t, 404, sc.resp.Code)
})
})
Convey("Given user has no admin permissions", func() {
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})
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 403)
})
setUp := func() {
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUp()
callGetDashboardPermissions(sc)
assert.Equal(t, 403, sc.resp.Code)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -57,18 +73,20 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
},
}
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
callUpdateDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 403)
})
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(sc)
assert.Equal(t, 403, sc.resp.Code)
})
})
Reset(func() {
t.Run("Given user has admin permissions and permissions to update", func(t *testing.T) {
origNewGuardian := guardian.New
t.Cleanup(func() {
guardian.New = origNewGuardian
})
})
Convey("Given user has admin permissions and permissions to update", func() {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanAdminValue: true,
CheckPermissionBeforeUpdateValue: true,
@ -81,21 +99,25 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
},
})
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
callGetDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(len(respJSON.MustArray()), ShouldEqual, 5)
So(respJSON.GetIndex(0).Get("userId").MustInt(), ShouldEqual, 2)
So(respJSON.GetIndex(0).Get("permission").MustInt(), ShouldEqual, models.PERMISSION_VIEW)
})
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
setUp()
callGetDashboardPermissions(sc)
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, 5, len(respJSON.MustArray()))
assert.Equal(t, 2, respJSON.GetIndex(0).Get("userId").MustInt())
assert.Equal(t, int(models.PERMISSION_VIEW), respJSON.GetIndex(0).Get("permission").MustInt())
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -103,29 +125,32 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
},
}
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
callUpdateDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 200)
})
Reset(func() {
guardian.New = origNewGuardian
})
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(sc)
assert.Equal(t, 200, sc.resp.Code)
})
})
Convey("When trying to update permissions with duplicate permissions", func() {
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,
})
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -133,29 +158,33 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
},
}
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
callUpdateDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 400)
})
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(sc)
assert.Equal(t, 400, sc.resp.Code)
})
})
Reset(func() {
t.Run("When trying to override inherited permissions with lower precedence", func(t *testing.T) {
origNewGuardian := guardian.New
t.Cleanup(func() {
guardian.New = origNewGuardian
})
})
Convey("When trying to override inherited permissions with lower precedence", func() {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanAdminValue: true,
CheckPermissionBeforeUpdateValue: false,
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
)
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
setUp := func() {
getDashboardQueryResult := models.NewDashboard("Dash")
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = getDashboardQueryResult
return nil
})
}
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
@ -163,14 +192,12 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
},
}
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
callUpdateDashboardPermissions(sc)
So(sc.resp.Code, ShouldEqual, 400)
})
Reset(func() {
guardian.New = origNewGuardian
})
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
setUp()
callUpdateDashboardPermissions(sc)
assert.Equal(t, 400, sc.resp.Code)
})
})
})
}
@ -188,16 +215,16 @@ func callUpdateDashboardPermissions(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
}
func updateDashboardPermissionScenario(desc string, url string, routePattern string, cmd dtos.UpdateDashboardAclCommand, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func updateDashboardPermissionScenario(t *testing.T, desc string, url string, routePattern string, cmd dtos.UpdateDashboardAclCommand, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.OrgId = TestOrgID
sc.context.UserId = TestUserID
sc.context.OrgId = testOrgID
sc.context.UserId = testUserID
return UpdateDashboardPermissions(c, cmd)
})

@ -236,10 +236,10 @@ func DeleteDashboardSnapshot(c *models.ReqContext) Response {
if err != nil {
return Error(500, "Failed to get dashboard snapshot", err)
}
if query.Result == nil {
return Error(404, "Failed to get dashboard snapshot", nil)
}
dashboard, err := query.Result.DashboardJSON()
if err != nil {
return Error(500, "Failed to get dashboard data for dashboard snapshot", err)

@ -4,23 +4,38 @@ import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
"github.com/grafana/grafana/pkg/components/securedata"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/securedata"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
)
func TestDashboardSnapshotApiEndpoint(t *testing.T) {
Convey("Given a single snapshot", t, func() {
var externalRequest *http.Request
jsonModel, _ := simplejson.NewJson([]byte(`{"id":100}`))
func TestDashboardSnapshotAPIEndpoint_singleSnapshot(t *testing.T) {
setupRemoteServer := func(fn func(http.ResponseWriter, *http.Request)) *httptest.Server {
s := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
fn(rw, r)
}))
t.Cleanup(s.Close)
return s
}
jsonModel, err := simplejson.NewJson([]byte(`{"id":100}`))
require.NoError(t, err)
viewerRole := models.ROLE_VIEWER
editorRole := models.ROLE_EDITOR
aclMockResp := []*models.DashboardAclInfoDTO{}
setUpSnapshotTest := func(t *testing.T) *models.DashboardSnapshot {
t.Helper()
mockSnapshotResult := &models.DashboardSnapshot{
Id: 1,
@ -41,9 +56,6 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
return nil
})
viewerRole := models.ROLE_VIEWER
editorRole := models.ROLE_EDITOR
aclMockResp := []*models.DashboardAclInfoDTO{}
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
query.Result = aclMockResp
return nil
@ -55,185 +67,199 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
return nil
})
setupRemoteServer := func(fn func(http.ResponseWriter, *http.Request)) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
fn(rw, r)
}))
}
Convey("When user has editor role and is not in the ACL", func() {
Convey("Should not be able to delete snapshot", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
externalRequest = req
})
return mockSnapshotResult
}
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
t.Run("When user has editor role and is not in the ACL", func(t *testing.T) {
loggedInUserScenarioWithRole(t, "Should not be able to delete snapshot when calling DELETE on",
"DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
So(sc.resp.Code, ShouldEqual, 403)
So(externalRequest, ShouldBeNil)
var externalRequest *http.Request
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
externalRequest = req
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
assert.Equal(t, 403, sc.resp.Code)
require.Nil(t, externalRequest)
})
})
})
Convey("When user is anonymous", func() {
Convey("Should be able to delete snapshot by deleteKey", func() {
anonymousUserScenario("When calling GET on", "GET", "/api/snapshots-delete/12345", "/api/snapshots-delete/:deleteKey", func(sc *scenarioContext) {
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(200)
externalRequest = req
})
t.Run("When user is anonymous", func(t *testing.T) {
anonymousUserScenario(t, "Should be able to delete a snapshot when calling GET on", "GET",
"/api/snapshots-delete/12345", "/api/snapshots-delete/:deleteKey", func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshotByDeleteKey
sc.fakeReqWithParams("GET", sc.url, map[string]string{"deleteKey": "12345"}).exec()
var externalRequest *http.Request
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(200)
externalRequest = req
})
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshotByDeleteKey
sc.fakeReqWithParams("GET", sc.url, map[string]string{"deleteKey": "12345"}).exec()
So(respJSON.Get("message").MustString(), ShouldStartWith, "Snapshot deleted")
require.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
So(externalRequest.Method, ShouldEqual, http.MethodGet)
So(fmt.Sprintf("http://%s", externalRequest.Host), ShouldEqual, ts.URL)
So(externalRequest.URL.EscapedPath(), ShouldEqual, "/")
})
assert.True(t, strings.HasPrefix(respJSON.Get("message").MustString(), "Snapshot deleted"))
assert.Equal(t, http.MethodGet, externalRequest.Method)
assert.Equal(t, ts.URL, fmt.Sprintf("http://%s", externalRequest.Host))
assert.Equal(t, "/", externalRequest.URL.EscapedPath())
})
})
})
Convey("When user is editor and dashboard has default ACL", func() {
aclMockResp = []*models.DashboardAclInfoDTO{
{Role: &viewerRole, Permission: models.PERMISSION_VIEW},
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
}
Convey("Should be able to delete a snapshot", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(200)
externalRequest = req
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("message").MustString(), ShouldStartWith, "Snapshot deleted")
So(fmt.Sprintf("http://%s", externalRequest.Host), ShouldEqual, ts.URL)
So(externalRequest.URL.EscapedPath(), ShouldEqual, "/")
t.Run("When user is editor and dashboard has default ACL", func(t *testing.T) {
aclMockResp = []*models.DashboardAclInfoDTO{
{Role: &viewerRole, Permission: models.PERMISSION_VIEW},
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
}
loggedInUserScenarioWithRole(t, "Should be able to delete a snapshot when calling DELETE on", "DELETE",
"/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
var externalRequest *http.Request
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(200)
externalRequest = req
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.True(t, strings.HasPrefix(respJSON.Get("message").MustString(), "Snapshot deleted"))
assert.Equal(t, ts.URL, fmt.Sprintf("http://%s", externalRequest.Host))
assert.Equal(t, "/", externalRequest.URL.EscapedPath())
})
})
})
Convey("When user is editor and is the creator of the snapshot", func() {
aclMockResp = []*models.DashboardAclInfoDTO{}
mockSnapshotResult.UserId = TestUserID
mockSnapshotResult.External = false
t.Run("When user is editor and creator of the snapshot", func(t *testing.T) {
aclMockResp = []*models.DashboardAclInfoDTO{}
loggedInUserScenarioWithRole(t, "Should be able to delete a snapshot when calling DELETE on",
"DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
Convey("Should be able to delete a snapshot", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
mockSnapshotResult.UserId = testUserID
mockSnapshotResult.External = false
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
So(respJSON.Get("message").MustString(), ShouldStartWith, "Snapshot deleted")
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.True(t, strings.HasPrefix(respJSON.Get("message").MustString(), "Snapshot deleted"))
})
})
t.Run("When deleting an external snapshot", func(t *testing.T) {
aclMockResp = []*models.DashboardAclInfoDTO{}
var writeErr error
loggedInUserScenarioWithRole(t,
"Should gracefully delete local snapshot when remote snapshot has already been removed when calling DELETE on",
"DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
mockSnapshotResult.UserId = testUserID
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
_, writeErr = rw.Write([]byte(`{"message":"Failed to get dashboard snapshot"}`))
rw.WriteHeader(500)
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
require.NoError(t, writeErr)
assert.Equal(t, 200, sc.resp.Code)
})
})
Convey("When deleting an external snapshot", func() {
aclMockResp = []*models.DashboardAclInfoDTO{}
mockSnapshotResult.UserId = TestUserID
loggedInUserScenarioWithRole(t,
"Should fail to delete local snapshot when an unexpected 500 error occurs when calling DELETE on", "DELETE",
"/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
mockSnapshotResult.UserId = testUserID
Convey("Should gracefully delete local snapshot when remote snapshot has already been removed", func() {
var writeErr error
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
_, writeErr = rw.Write([]byte(`{"message":"Failed to get dashboard snapshot"}`))
rw.WriteHeader(500)
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
So(writeErr, ShouldBeNil)
So(sc.resp.Code, ShouldEqual, 200)
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(500)
_, writeErr = rw.Write([]byte(`{"message":"Unexpected"}`))
})
})
Convey("Should fail to delete local snapshot when an unexpected 500 error occurs", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
var writeErr error
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(500)
_, writeErr = rw.Write([]byte(`{"message":"Unexpected"}`))
})
t.Log("Setting external delete URL", "url", ts.URL)
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
So(writeErr, ShouldBeNil)
So(sc.resp.Code, ShouldEqual, 500)
})
require.NoError(t, writeErr)
assert.Equal(t, 500, sc.resp.Code)
})
Convey("Should fail to delete local snapshot when an unexpected remote error occurs", func() {
loggedInUserScenarioWithRole("When calling DELETE on", "DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(404)
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
loggedInUserScenarioWithRole(t,
"Should fail to delete local snapshot when an unexpected remote error occurs when calling DELETE on",
"DELETE", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
mockSnapshotResult := setUpSnapshotTest(t)
mockSnapshotResult.UserId = testUserID
So(sc.resp.Code, ShouldEqual, 500)
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
rw.WriteHeader(404)
})
mockSnapshotResult.ExternalDeleteUrl = ts.URL
sc.handlerFunc = DeleteDashboardSnapshot
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
assert.Equal(t, 500, sc.resp.Code)
})
Convey("Should be able to read a snapshot's un-encrypted data", func() {
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
sc.handlerFunc = GetDashboardSnapshot
sc.fakeReqWithParams("GET", sc.url, map[string]string{"key": "12345"}).exec()
loggedInUserScenarioWithRole(t, "Should be able to read a snapshot's unencrypted data when calling GET on",
"GET", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
setUpSnapshotTest(t)
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
sc.handlerFunc = GetDashboardSnapshot
sc.fakeReqWithParams("GET", sc.url, map[string]string{"key": "12345"}).exec()
dashboard := respJSON.Get("dashboard")
id := dashboard.Get("id")
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
So(id.MustInt64(), ShouldEqual, 100)
})
dashboard := respJSON.Get("dashboard")
id := dashboard.Get("id")
assert.Equal(t, int64(100), id.MustInt64())
})
Convey("Should be able to read a snapshot's encrypted data", func() {
loggedInUserScenarioWithRole(t, "Should be able to read a snapshot's encrypted data When calling GET on",
"GET", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
origSecret := setting.SecretKey
setting.SecretKey = "dashboard_snapshot_api_test"
t.Cleanup(func() {
setting.SecretKey = origSecret
})
dashboardId := 123
jsonModel, err := simplejson.NewJson([]byte(fmt.Sprintf(`{"id":%d}`, dashboardId)))
So(err, ShouldBeNil)
const dashboardID int64 = 123
jsonModel, err := simplejson.NewJson([]byte(fmt.Sprintf(`{"id":%d}`, dashboardID)))
require.NoError(t, err)
jsonModelEncoded, err := jsonModel.Encode()
So(err, ShouldBeNil)
require.NoError(t, err)
encrypted, err := securedata.Encrypt(jsonModelEncoded)
So(err, ShouldBeNil)
require.NoError(t, err)
// mock snapshot with encrypted dashboard info
mockSnapshotResult := &models.DashboardSnapshot{
@ -242,21 +268,20 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
Expires: time.Now().Add(time.Duration(1000) * time.Second),
}
setUpSnapshotTest(t)
bus.AddHandler("test", func(query *models.GetDashboardSnapshotQuery) error {
query.Result = mockSnapshotResult
return nil
})
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/snapshots/12345", "/api/snapshots/:key", models.ROLE_EDITOR, func(sc *scenarioContext) {
sc.handlerFunc = GetDashboardSnapshot
sc.fakeReqWithParams("GET", sc.url, map[string]string{"key": "12345"}).exec()
sc.handlerFunc = GetDashboardSnapshot
sc.fakeReqWithParams("GET", sc.url, map[string]string{"key": "12345"}).exec()
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("dashboard").Get("id").MustInt64(), ShouldEqual, dashboardId)
})
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, dashboardID, respJSON.Get("dashboard").Get("id").MustInt64())
})
})
})
}

File diff suppressed because it is too large Load Diff

@ -6,64 +6,57 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/grafana/grafana/pkg/bus"
. "github.com/smartystreets/goconvey/convey"
)
const (
TestOrgID = 1
TestUserID = 1
testOrgID int64 = 1
testUserID int64 = 1
)
func TestDataSourcesProxy(t *testing.T) {
Convey("Given a user is logged in", t, func() {
loggedInUserScenario("When calling GET on", "/api/datasources/", func(sc *scenarioContext) {
// Stubs the database query
bus.AddHandler("test", func(query *models.GetDataSourcesQuery) error {
So(query.OrgId, ShouldEqual, TestOrgID)
query.Result = []*models.DataSource{
{Name: "mmm"},
{Name: "ZZZ"},
{Name: "BBB"},
{Name: "aaa"},
}
return nil
})
// handler func being tested
sc.handlerFunc = GetDataSources
sc.fakeReq("GET", "/api/datasources").exec()
respJSON := []map[string]interface{}{}
err := json.NewDecoder(sc.resp.Body).Decode(&respJSON)
So(err, ShouldBeNil)
Convey("should return list of datasources for org sorted alphabetically and case insensitively", func() {
So(respJSON[0]["name"], ShouldEqual, "aaa")
So(respJSON[1]["name"], ShouldEqual, "BBB")
So(respJSON[2]["name"], ShouldEqual, "mmm")
So(respJSON[3]["name"], ShouldEqual, "ZZZ")
})
func TestDataSourcesProxy_userLoggedIn(t *testing.T) {
loggedInUserScenario(t, "When calling GET on", "/api/datasources/", func(sc *scenarioContext) {
// Stubs the database query
bus.AddHandler("test", func(query *models.GetDataSourcesQuery) error {
assert.Equal(t, testOrgID, query.OrgId)
query.Result = []*models.DataSource{
{Name: "mmm"},
{Name: "ZZZ"},
{Name: "BBB"},
{Name: "aaa"},
}
return nil
})
Convey("Should be able to save a data source", func() {
loggedInUserScenario("When calling DELETE on non-existing", "/api/datasources/name/12345", func(sc *scenarioContext) {
sc.handlerFunc = DeleteDataSourceByName
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
})
})
// handler func being tested
sc.handlerFunc = GetDataSources
sc.fakeReq("GET", "/api/datasources").exec()
respJSON := []map[string]interface{}{}
err := json.NewDecoder(sc.resp.Body).Decode(&respJSON)
require.NoError(t, err)
assert.Equal(t, "aaa", respJSON[0]["name"])
assert.Equal(t, "BBB", respJSON[1]["name"])
assert.Equal(t, "mmm", respJSON[2]["name"])
assert.Equal(t, "ZZZ", respJSON[3]["name"])
})
loggedInUserScenario(t, "Should be able to save a data source when calling DELETE on non-existing",
"/api/datasources/name/12345", func(sc *scenarioContext) {
sc.handlerFunc = DeleteDataSourceByName
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
})
}
// Adding data sources with invalid URLs should lead to an error.
func TestAddDataSource_InvalidURL(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext("/api/datasources")
// TODO: Make this an argument to setupScenarioContext
sc.t = t
sc := setupScenarioContext(t, "/api/datasources")
sc.m.Post(sc.url, Wrap(func(c *models.ReqContext) Response {
return AddDataSource(c, models.AddDataSourceCommand{
@ -93,9 +86,7 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
return nil
})
sc := setupScenarioContext("/api/datasources")
// TODO: Make this an argument to setupScenarioContext
sc.t = t
sc := setupScenarioContext(t, "/api/datasources")
sc.m.Post(sc.url, Wrap(func(c *models.ReqContext) Response {
return AddDataSource(c, models.AddDataSourceCommand{
@ -113,9 +104,7 @@ func TestAddDataSource_URLWithoutProtocol(t *testing.T) {
func TestUpdateDataSource_InvalidURL(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext("/api/datasources/1234")
// TODO: Make this an argument to setupScenarioContext
sc.t = t
sc := setupScenarioContext(t, "/api/datasources/1234")
sc.m.Put(sc.url, Wrap(func(c *models.ReqContext) Response {
return AddDataSource(c, models.AddDataSourceCommand{
@ -145,9 +134,7 @@ func TestUpdateDataSource_URLWithoutProtocol(t *testing.T) {
return nil
})
sc := setupScenarioContext("/api/datasources/1234")
// TODO: Make this an argument to setupScenarioContext
sc.t = t
sc := setupScenarioContext(t, "/api/datasources/1234")
sc.m.Put(sc.url, Wrap(func(c *models.ReqContext) Response {
return AddDataSource(c, models.AddDataSourceCommand{

@ -1,6 +1,7 @@
package api
import (
"fmt"
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
@ -9,204 +10,201 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/guardian"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFolderPermissionApiEndpoint(t *testing.T) {
Convey("Folder permissions test", t, func() {
Convey("Given folder not exists", func() {
mock := &fakeFolderService{
GetFolderByUIDError: models.ErrFolderNotFound,
}
origNewFolderService := dashboards.NewFolderService
mockFolderService(mock)
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 404)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 404)
})
Reset(func() {
dashboards.NewFolderService = origNewFolderService
})
})
Convey("Given user has no admin permissions", func() {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
origNewFolderService := dashboards.NewFolderService
mockFolderService(mock)
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 403)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 403)
})
Reset(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
})
Convey("Given user has admin permissions and permissions to update", func() {
origNewGuardian := guardian.New
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},
},
})
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
origNewFolderService := dashboards.NewFolderService
mockFolderService(mock)
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
callGetFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(len(respJSON.MustArray()), ShouldEqual, 5)
So(respJSON.GetIndex(0).Get("userId").MustInt(), ShouldEqual, 2)
So(respJSON.GetIndex(0).Get("permission").MustInt(), ShouldEqual, models.PERMISSION_VIEW)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 200)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(respJSON.Get("id").MustInt(), ShouldEqual, 1)
So(respJSON.Get("title").MustString(), ShouldEqual, "Folder")
})
Reset(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
})
Convey("When trying to update permissions with duplicate permissions", func() {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanAdminValue: true,
CheckPermissionBeforeUpdateValue: false,
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
})
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
origNewFolderService := dashboards.NewFolderService
mockFolderService(mock)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 400)
})
Reset(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
})
Convey("When trying to override inherited permissions with lower precedence", func() {
origNewGuardian := guardian.New
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanAdminValue: true,
CheckPermissionBeforeUpdateValue: false,
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
)
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
origNewFolderService := dashboards.NewFolderService
mockFolderService(mock)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
So(sc.resp.Code, ShouldEqual, 400)
})
Reset(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
func TestFolderPermissionAPIEndpoint(t *testing.T) {
t.Run("Given folder not exists", func(t *testing.T) {
mock := &fakeFolderService{
GetFolderByUIDError: models.ErrFolderNotFound,
}
origNewFolderService := dashboards.NewFolderService
t.Cleanup(func() {
dashboards.NewFolderService = origNewFolderService
})
mockFolderService(mock)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetFolderPermissions(sc)
assert.Equal(t, 404, sc.resp.Code)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
assert.Equal(t, 404, sc.resp.Code)
})
})
t.Run("Given user has no admin permissions", func(t *testing.T) {
origNewGuardian := guardian.New
origNewFolderService := dashboards.NewFolderService
t.Cleanup(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
mockFolderService(mock)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
callGetFolderPermissions(sc)
assert.Equal(t, 403, sc.resp.Code)
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
assert.Equal(t, 403, sc.resp.Code)
})
})
t.Run("Given user has admin permissions and permissions to update", func(t *testing.T) {
origNewGuardian := guardian.New
origNewFolderService := dashboards.NewFolderService
t.Cleanup(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
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},
},
})
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
mockFolderService(mock)
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_ADMIN, func(sc *scenarioContext) {
callGetFolderPermissions(sc)
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, 5, len(respJSON.MustArray()))
assert.Equal(t, 2, respJSON.GetIndex(0).Get("userId").MustInt())
assert.Equal(t, int(models.PERMISSION_VIEW), respJSON.GetIndex(0).Get("permission").MustInt())
})
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
assert.Equal(t, 200, sc.resp.Code)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, 1, respJSON.Get("id").MustInt())
assert.Equal(t, "Folder", respJSON.Get("title").MustString())
})
})
t.Run("When trying to update permissions with duplicate permissions", func(t *testing.T) {
origNewGuardian := guardian.New
origNewFolderService := dashboards.NewFolderService
t.Cleanup(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanAdminValue: true,
CheckPermissionBeforeUpdateValue: false,
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
})
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
mockFolderService(mock)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
assert.Equal(t, 400, sc.resp.Code)
})
})
t.Run("When trying to override inherited permissions with lower precedence", func(t *testing.T) {
origNewGuardian := guardian.New
origNewFolderService := dashboards.NewFolderService
t.Cleanup(func() {
guardian.New = origNewGuardian
dashboards.NewFolderService = origNewFolderService
})
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
CanAdminValue: true,
CheckPermissionBeforeUpdateValue: false,
CheckPermissionBeforeUpdateError: guardian.ErrGuardianOverride},
)
mock := &fakeFolderService{
GetFolderByUIDResult: &models.Folder{
Id: 1,
Uid: "uid",
Title: "Folder",
},
}
mockFolderService(mock)
cmd := dtos.UpdateDashboardAclCommand{
Items: []dtos.DashboardAclUpdateItem{
{UserId: 1000, Permission: models.PERMISSION_ADMIN},
},
}
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
callUpdateFolderPermissions(sc)
assert.Equal(t, 400, sc.resp.Code)
})
})
}
@ -224,16 +222,16 @@ func callUpdateFolderPermissions(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
}
func updateFolderPermissionScenario(desc string, url string, routePattern string, cmd dtos.UpdateDashboardAclCommand, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func updateFolderPermissionScenario(t *testing.T, desc string, url string, routePattern string, cmd dtos.UpdateDashboardAclCommand, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.OrgId = TestOrgID
sc.context.UserId = TestUserID
sc.context.OrgId = testOrgID
sc.context.UserId = testUserID
return UpdateFolderPermissions(c, cmd)
})

@ -10,127 +10,121 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/setting"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFoldersApiEndpoint(t *testing.T) {
Convey("Create/update folder response tests", t, func() {
Convey("Given a correct request for creating a folder", func() {
cmd := models.CreateFolderCommand{
Uid: "uid",
Title: "Folder",
}
func TestFoldersAPIEndpoint(t *testing.T) {
t.Run("Given a correct request for creating a folder", func(t *testing.T) {
cmd := models.CreateFolderCommand{
Uid: "uid",
Title: "Folder",
}
mock := &fakeFolderService{
CreateFolderResult: &models.Folder{Id: 1, Uid: "uid", Title: "Folder"},
}
mock := &fakeFolderService{
CreateFolderResult: &models.Folder{Id: 1, Uid: "uid", Title: "Folder"},
}
createFolderScenario("When calling POST on", "/api/folders", "/api/folders", mock, cmd, func(sc *scenarioContext) {
createFolderScenario(t, "When calling POST on", "/api/folders", "/api/folders", mock, cmd,
func(sc *scenarioContext) {
callCreateFolder(sc)
Convey("It should return correct response data", func() {
folder := dtos.Folder{}
err := json.NewDecoder(sc.resp.Body).Decode(&folder)
So(err, ShouldBeNil)
So(folder.Id, ShouldEqual, 1)
So(folder.Uid, ShouldEqual, "uid")
So(folder.Title, ShouldEqual, "Folder")
})
folder := dtos.Folder{}
err := json.NewDecoder(sc.resp.Body).Decode(&folder)
require.NoError(t, err)
assert.Equal(t, int64(1), folder.Id)
assert.Equal(t, "uid", folder.Uid)
assert.Equal(t, "Folder", folder.Title)
})
})
})
Convey("Given incorrect requests for creating a folder", func() {
testCases := []struct {
Error error
ExpectedStatusCode int
}{
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 400},
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 400},
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
}
t.Run("Given incorrect requests for creating a folder", func(t *testing.T) {
testCases := []struct {
Error error
ExpectedStatusCode int
}{
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 400},
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 400},
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
}
cmd := models.CreateFolderCommand{
Uid: "uid",
Title: "Folder",
}
cmd := models.CreateFolderCommand{
Uid: "uid",
Title: "Folder",
}
for _, tc := range testCases {
mock := &fakeFolderService{
CreateFolderError: tc.Error,
}
for _, tc := range testCases {
mock := &fakeFolderService{
CreateFolderError: tc.Error,
}
createFolderScenario(fmt.Sprintf("Expect '%s' error when calling POST on", tc.Error.Error()), "/api/folders", "/api/folders", mock, cmd, func(sc *scenarioContext) {
createFolderScenario(t, fmt.Sprintf("Expect '%s' error when calling POST on", tc.Error.Error()),
"/api/folders", "/api/folders", mock, cmd, func(sc *scenarioContext) {
callCreateFolder(sc)
if sc.resp.Code != tc.ExpectedStatusCode {
t.Errorf("For error '%s' expected status code %d, actual %d", tc.Error, tc.ExpectedStatusCode, sc.resp.Code)
}
assert.Equalf(t, tc.ExpectedStatusCode, sc.resp.Code, "Wrong status code for error %s", tc.Error)
})
}
})
}
})
Convey("Given a correct request for updating a folder", func() {
cmd := models.UpdateFolderCommand{
Title: "Folder upd",
}
t.Run("Given a correct request for updating a folder", func(t *testing.T) {
cmd := models.UpdateFolderCommand{
Title: "Folder upd",
}
mock := &fakeFolderService{
UpdateFolderResult: &models.Folder{Id: 1, Uid: "uid", Title: "Folder upd"},
}
mock := &fakeFolderService{
UpdateFolderResult: &models.Folder{Id: 1, Uid: "uid", Title: "Folder upd"},
}
updateFolderScenario("When calling PUT on", "/api/folders/uid", "/api/folders/:uid", mock, cmd, func(sc *scenarioContext) {
updateFolderScenario(t, "When calling PUT on", "/api/folders/uid", "/api/folders/:uid", mock, cmd,
func(sc *scenarioContext) {
callUpdateFolder(sc)
Convey("It should return correct response data", func() {
folder := dtos.Folder{}
err := json.NewDecoder(sc.resp.Body).Decode(&folder)
So(err, ShouldBeNil)
So(folder.Id, ShouldEqual, 1)
So(folder.Uid, ShouldEqual, "uid")
So(folder.Title, ShouldEqual, "Folder upd")
})
folder := dtos.Folder{}
err := json.NewDecoder(sc.resp.Body).Decode(&folder)
require.NoError(t, err)
assert.Equal(t, int64(1), folder.Id)
assert.Equal(t, "uid", folder.Uid)
assert.Equal(t, "Folder upd", folder.Title)
})
})
})
Convey("Given incorrect requests for updating a folder", func() {
testCases := []struct {
Error error
ExpectedStatusCode int
}{
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 400},
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 400},
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
}
t.Run("Given incorrect requests for updating a folder", func(t *testing.T) {
testCases := []struct {
Error error
ExpectedStatusCode int
}{
{Error: models.ErrFolderWithSameUIDExists, ExpectedStatusCode: 400},
{Error: models.ErrFolderTitleEmpty, ExpectedStatusCode: 400},
{Error: models.ErrFolderSameNameExists, ExpectedStatusCode: 400},
{Error: models.ErrDashboardInvalidUid, ExpectedStatusCode: 400},
{Error: models.ErrDashboardUidTooLong, ExpectedStatusCode: 400},
{Error: models.ErrFolderAccessDenied, ExpectedStatusCode: 403},
{Error: models.ErrFolderNotFound, ExpectedStatusCode: 404},
{Error: models.ErrFolderVersionMismatch, ExpectedStatusCode: 412},
{Error: models.ErrFolderFailedGenerateUniqueUid, ExpectedStatusCode: 500},
}
cmd := models.UpdateFolderCommand{
Title: "Folder upd",
}
cmd := models.UpdateFolderCommand{
Title: "Folder upd",
}
for _, tc := range testCases {
mock := &fakeFolderService{
UpdateFolderError: tc.Error,
}
for _, tc := range testCases {
mock := &fakeFolderService{
UpdateFolderError: tc.Error,
}
updateFolderScenario(fmt.Sprintf("Expect '%s' error when calling PUT on", tc.Error.Error()), "/api/folders/uid", "/api/folders/:uid", mock, cmd, func(sc *scenarioContext) {
updateFolderScenario(t, fmt.Sprintf("Expect '%s' error when calling PUT on", tc.Error.Error()),
"/api/folders/uid", "/api/folders/:uid", mock, cmd, func(sc *scenarioContext) {
callUpdateFolder(sc)
if sc.resp.Code != tc.ExpectedStatusCode {
t.Errorf("For error '%s' expected status code %d, actual %d", tc.Error, tc.ExpectedStatusCode, sc.resp.Code)
}
assert.Equalf(t, tc.ExpectedStatusCode, sc.resp.Code, "Wrong status code for %s", tc.Error)
})
}
})
}
})
}
@ -138,19 +132,20 @@ func callCreateFolder(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
}
func createFolderScenario(desc string, url string, routePattern string, mock *fakeFolderService, cmd models.CreateFolderCommand, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func createFolderScenario(t *testing.T, desc string, url string, routePattern string, mock *fakeFolderService,
cmd models.CreateFolderCommand, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
hs := HTTPServer{
Bus: bus.GetBus(),
Cfg: setting.NewCfg(),
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
return hs.CreateFolder(c, cmd)
})
@ -172,27 +167,27 @@ func callUpdateFolder(sc *scenarioContext) {
sc.fakeReqWithParams("PUT", sc.url, map[string]string{}).exec()
}
func updateFolderScenario(desc string, url string, routePattern string, mock *fakeFolderService, cmd models.UpdateFolderCommand, fn scenarioFunc) {
Convey(desc+" "+url, func() {
func updateFolderScenario(t *testing.T, desc string, url string, routePattern string, mock *fakeFolderService,
cmd models.UpdateFolderCommand, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.SignedInUser = &models.SignedInUser{OrgId: TestOrgID, UserId: TestUserID}
sc.context.SignedInUser = &models.SignedInUser{OrgId: testOrgID, UserId: testUserID}
return UpdateFolder(c, cmd)
})
origNewFolderService := dashboards.NewFolderService
t.Cleanup(func() {
dashboards.NewFolderService = origNewFolderService
})
mockFolderService(mock)
sc.m.Put(routePattern, sc.defaultHandler)
defer func() {
dashboards.NewFolderService = origNewFolderService
}()
fn(sc)
})
}

@ -27,7 +27,7 @@ func logSentryEventScenario(t *testing.T, desc string, event frontendSentryEvent
frontendLogger.SetHandler(origHandler)
})
sc := setupScenarioContext("/log")
sc := setupScenarioContext(t, "/log")
hs := HTTPServer{}
handler := Wrap(func(w http.ResponseWriter, c *models.ReqContext) Response {

@ -158,7 +158,6 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) Response {
}
ldapConfig, err := getLDAPConfig()
if err != nil {
return Error(http.StatusBadRequest, "Failed to obtain the LDAP configuration. Please verify the configuration and try again", err)
}

@ -50,11 +50,11 @@ func (m *LDAPMock) User(login string) (*models.ExternalUserInfo, ldap.ServerConf
func getUserFromLDAPContext(t *testing.T, requestURL string) *scenarioContext {
t.Helper()
sc := setupScenarioContext(requestURL)
sc := setupScenarioContext(t, requestURL)
ldap := setting.LDAPEnabled
origLDAP := setting.LDAPEnabled
setting.LDAPEnabled = true
defer func() { setting.LDAPEnabled = ldap }()
t.Cleanup(func() { setting.LDAPEnabled = origLDAP })
hs := &HTTPServer{Cfg: setting.NewCfg()}
@ -73,7 +73,7 @@ func getUserFromLDAPContext(t *testing.T, requestURL string) *scenarioContext {
return sc
}
func TestGetUserFromLDAPApiEndpoint_UserNotFound(t *testing.T) {
func TestGetUserFromLDAPAPIEndpoint_UserNotFound(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
@ -90,7 +90,7 @@ func TestGetUserFromLDAPApiEndpoint_UserNotFound(t *testing.T) {
assert.JSONEq(t, "{\"message\":\"No user was found in the LDAP server(s) with that username\"}", sc.resp.Body.String())
}
func TestGetUserFromLDAPApiEndpoint_OrgNotfound(t *testing.T) {
func TestGetUserFromLDAPAPIEndpoint_OrgNotfound(t *testing.T) {
isAdmin := true
userSearchResult = &models.ExternalUserInfo{
Name: "John Doe",
@ -152,7 +152,7 @@ func TestGetUserFromLDAPApiEndpoint_OrgNotfound(t *testing.T) {
assert.JSONEq(t, expected, sc.resp.Body.String())
}
func TestGetUserFromLDAPApiEndpoint(t *testing.T) {
func TestGetUserFromLDAPAPIEndpoint(t *testing.T) {
isAdmin := true
userSearchResult = &models.ExternalUserInfo{
Name: "John Doe",
@ -232,7 +232,7 @@ func TestGetUserFromLDAPApiEndpoint(t *testing.T) {
assert.JSONEq(t, expected, sc.resp.Body.String())
}
func TestGetUserFromLDAPApiEndpoint_WithTeamHandler(t *testing.T) {
func TestGetUserFromLDAPAPIEndpoint_WithTeamHandler(t *testing.T) {
isAdmin := true
userSearchResult = &models.ExternalUserInfo{
Name: "John Doe",
@ -319,11 +319,11 @@ func getLDAPStatusContext(t *testing.T) *scenarioContext {
t.Helper()
requestURL := "/api/admin/ldap/status"
sc := setupScenarioContext(requestURL)
sc := setupScenarioContext(t, requestURL)
ldap := setting.LDAPEnabled
setting.LDAPEnabled = true
defer func() { setting.LDAPEnabled = ldap }()
t.Cleanup(func() { setting.LDAPEnabled = ldap })
hs := &HTTPServer{Cfg: setting.NewCfg()}
@ -342,7 +342,7 @@ func getLDAPStatusContext(t *testing.T) *scenarioContext {
return sc
}
func TestGetLDAPStatusApiEndpoint(t *testing.T) {
func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
pingResult = []*multildap.ServerStatus{
{Host: "10.0.0.3", Port: 361, Available: true, Error: nil},
{Host: "10.0.0.3", Port: 362, Available: true, Error: nil},
@ -375,16 +375,21 @@ func TestGetLDAPStatusApiEndpoint(t *testing.T) {
// PostSyncUserWithLDAP tests
// ***
func postSyncUserWithLDAPContext(t *testing.T, requestURL string) *scenarioContext {
func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(t *testing.T)) *scenarioContext {
t.Helper()
sc := setupScenarioContext(requestURL)
sc := setupScenarioContext(t, requestURL)
ldap := setting.LDAPEnabled
t.Cleanup(func() {
setting.LDAPEnabled = ldap
})
setting.LDAPEnabled = true
defer func() { setting.LDAPEnabled = ldap }()
hs := &HTTPServer{Cfg: setting.NewCfg(), AuthTokenService: auth.NewFakeUserAuthTokenService()}
hs := &HTTPServer{
Cfg: setting.NewCfg(),
AuthTokenService: auth.NewFakeUserAuthTokenService(),
}
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
@ -394,7 +399,11 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string) *scenarioConte
sc.m.Post("/api/admin/ldap/sync/:id", sc.defaultHandler)
sc.resp = httptest.NewRecorder()
req, _ := http.NewRequest(http.MethodPost, requestURL, nil)
req, err := http.NewRequest(http.MethodPost, requestURL, nil)
require.NoError(t, err)
preHook(t)
sc.req = req
sc.exec()
@ -402,39 +411,39 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string) *scenarioConte
}
func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
userSearchResult = &models.ExternalUserInfo{
Login: "ldap-daniel",
}
userSearchResult = &models.ExternalUserInfo{
Login: "ldap-daniel",
}
bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
require.Equal(t, "ldap-daniel", cmd.ExternalUser.Login)
return nil
})
bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
require.Equal(t, "ldap-daniel", cmd.ExternalUser.Login)
return nil
})
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
q.Result = &models.User{Login: "ldap-daniel", Id: 34}
return nil
})
q.Result = &models.User{Login: "ldap-daniel", Id: 34}
return nil
})
bus.AddHandler("test", func(q *models.GetAuthInfoQuery) error {
require.Equal(t, q.UserId, int64(34))
require.Equal(t, q.AuthModule, models.AuthModuleLDAP)
bus.AddHandler("test", func(q *models.GetAuthInfoQuery) error {
require.Equal(t, q.UserId, int64(34))
require.Equal(t, q.AuthModule, models.AuthModuleLDAP)
return nil
return nil
})
})
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
assert.Equal(t, http.StatusOK, sc.resp.Code)
expected := `
@ -447,22 +456,22 @@ func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
}
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
return models.ErrUserNotFound
return models.ErrUserNotFound
})
})
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
assert.Equal(t, http.StatusNotFound, sc.resp.Code)
expected := `
@ -475,36 +484,36 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
}
func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
userSearchError = multildap.ErrDidNotFindUser
userSearchError = multildap.ErrDidNotFindUser
admin := setting.AdminUser
setting.AdminUser = "ldap-daniel"
defer func() { setting.AdminUser = admin }()
admin := setting.AdminUser
t.Cleanup(func() { setting.AdminUser = admin })
setting.AdminUser = "ldap-daniel"
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
q.Result = &models.User{Login: "ldap-daniel", Id: 34}
return nil
})
q.Result = &models.User{Login: "ldap-daniel", Id: 34}
return nil
})
bus.AddHandler("test", func(q *models.GetAuthInfoQuery) error {
require.Equal(t, q.UserId, int64(34))
require.Equal(t, q.AuthModule, models.AuthModuleLDAP)
bus.AddHandler("test", func(q *models.GetAuthInfoQuery) error {
require.Equal(t, q.UserId, int64(34))
require.Equal(t, q.AuthModule, models.AuthModuleLDAP)
return nil
return nil
})
})
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
expected := `
@ -518,42 +527,42 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
}
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
getLDAPConfig = func() (*ldap.Config, error) {
return &ldap.Config{}, nil
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
newLDAP = func(_ []*ldap.ServerConfig) multildap.IMultiLDAP {
return &LDAPMock{}
}
userSearchResult = nil
userSearchResult = nil
bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
require.Equal(t, "ldap-daniel", cmd.ExternalUser.Login)
return nil
})
bus.AddHandler("test", func(cmd *models.UpsertUserCommand) error {
require.Equal(t, "ldap-daniel", cmd.ExternalUser.Login)
return nil
})
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
q.Result = &models.User{Login: "ldap-daniel", Id: 34}
return nil
})
q.Result = &models.User{Login: "ldap-daniel", Id: 34}
return nil
})
bus.AddHandler("test", func(q *models.GetExternalUserInfoByLoginQuery) error {
assert.Equal(t, "ldap-daniel", q.LoginOrEmail)
q.Result = &models.ExternalUserInfo{IsDisabled: true, UserId: 34}
bus.AddHandler("test", func(q *models.GetExternalUserInfoByLoginQuery) error {
assert.Equal(t, "ldap-daniel", q.LoginOrEmail)
q.Result = &models.ExternalUserInfo{IsDisabled: true, UserId: 34}
return nil
})
return nil
})
bus.AddHandler("test", func(cmd *models.DisableUserCommand) error {
assert.Equal(t, 34, cmd.UserId)
return nil
bus.AddHandler("test", func(cmd *models.DisableUserCommand) error {
assert.Equal(t, 34, cmd.UserId)
return nil
})
})
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
expected := `

@ -53,7 +53,7 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
// when using a subUrl, the redirect_to should start with the subUrl (which contains the leading slash), otherwise the redirect
// will send the user to the wrong location
if hs.Cfg.AppSubUrl != "" && !strings.HasPrefix(to.Path, hs.Cfg.AppSubUrl+"/") {
if hs.Cfg.AppSubURL != "" && !strings.HasPrefix(to.Path, hs.Cfg.AppSubURL+"/") {
return login.ErrInvalidRedirectTo
}
@ -62,8 +62,8 @@ func (hs *HTTPServer) ValidateRedirectTo(redirectTo string) error {
func (hs *HTTPServer) CookieOptionsFromCfg() middleware.CookieOptions {
path := "/"
if len(hs.Cfg.AppSubUrl) > 0 {
path = hs.Cfg.AppSubUrl
if len(hs.Cfg.AppSubURL) > 0 {
path = hs.Cfg.AppSubURL
}
return middleware.CookieOptions{
Path: path,
@ -126,7 +126,7 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
// the user is already logged so instead of rendering the login page with error
// it should be redirected to the home page.
log.Debugf("Ignored invalid redirect_to cookie value: %v", redirectTo)
redirectTo = hs.Cfg.AppSubUrl + "/"
redirectTo = hs.Cfg.AppSubURL + "/"
}
middleware.DeleteCookie(c.Resp, "redirect_to", hs.CookieOptionsFromCfg)
c.Redirect(redirectTo)

@ -84,7 +84,7 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
mockViewIndex()
defer resetViewIndex()
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
License: &licensing.OSSLicensingService{},
@ -138,7 +138,7 @@ func TestLoginViewRedirect(t *testing.T) {
mockViewIndex()
defer resetViewIndex()
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
License: &licensing.OSSLicensingService{},
@ -254,12 +254,12 @@ func TestLoginViewRedirect(t *testing.T) {
}
for _, c := range redirectCases {
hs.Cfg.AppUrl = c.appURL
hs.Cfg.AppSubUrl = c.appSubURL
hs.Cfg.AppURL = c.appURL
hs.Cfg.AppSubURL = c.appSubURL
t.Run(c.desc, func(t *testing.T) {
expCookiePath := "/"
if len(hs.Cfg.AppSubUrl) > 0 {
expCookiePath = hs.Cfg.AppSubUrl
if len(hs.Cfg.AppSubURL) > 0 {
expCookiePath = hs.Cfg.AppSubURL
}
cookie := http.Cookie{
Name: "redirect_to",
@ -314,7 +314,7 @@ func TestLoginPostRedirect(t *testing.T) {
mockViewIndex()
defer resetViewIndex()
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
log: &FakeLogger{},
Cfg: setting.NewCfg(),
@ -423,12 +423,12 @@ func TestLoginPostRedirect(t *testing.T) {
}
for _, c := range redirectCases {
hs.Cfg.AppUrl = c.appURL
hs.Cfg.AppSubUrl = c.appSubURL
hs.Cfg.AppURL = c.appURL
hs.Cfg.AppSubURL = c.appSubURL
t.Run(c.desc, func(t *testing.T) {
expCookiePath := "/"
if len(hs.Cfg.AppSubUrl) > 0 {
expCookiePath = hs.Cfg.AppSubUrl
if len(hs.Cfg.AppSubURL) > 0 {
expCookiePath = hs.Cfg.AppSubURL
}
cookie := http.Cookie{
Name: "redirect_to",
@ -472,7 +472,7 @@ func TestLoginOAuthRedirect(t *testing.T) {
mockSetIndexViewData()
defer resetSetIndexViewData()
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
License: &licensing.OSSLicensingService{},
@ -507,7 +507,7 @@ func TestLoginInternal(t *testing.T) {
mockViewIndex()
defer resetViewIndex()
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
License: &licensing.OSSLicensingService{},
@ -537,7 +537,7 @@ func TestLoginInternal(t *testing.T) {
}
func TestAuthProxyLoginEnableLoginTokenDisabled(t *testing.T) {
sc := setupAuthProxyLoginTest(false)
sc := setupAuthProxyLoginTest(t, false)
assert.Equal(t, sc.resp.Code, 302)
location, ok := sc.resp.Header()["Location"]
@ -549,7 +549,7 @@ func TestAuthProxyLoginEnableLoginTokenDisabled(t *testing.T) {
}
func TestAuthProxyLoginWithEnableLoginToken(t *testing.T) {
sc := setupAuthProxyLoginTest(true)
sc := setupAuthProxyLoginTest(t, true)
assert.Equal(t, sc.resp.Code, 302)
location, ok := sc.resp.Header()["Location"]
@ -561,11 +561,11 @@ func TestAuthProxyLoginWithEnableLoginToken(t *testing.T) {
assert.Equal(t, "grafana_session=; Path=/; Max-Age=0; HttpOnly", setCookie[0])
}
func setupAuthProxyLoginTest(enableLoginToken bool) *scenarioContext {
func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioContext {
mockSetIndexViewData()
defer resetSetIndexViewData()
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
License: &licensing.OSSLicensingService{},
@ -601,7 +601,7 @@ func (r *loginHookTest) LoginHook(loginInfo *models.LoginInfo, req *models.ReqCo
}
func TestLoginPostRunLokingHook(t *testing.T) {
sc := setupScenarioContext("/login")
sc := setupScenarioContext(t, "/login")
hookService := &hooks.HooksService{}
hs := &HTTPServer{
log: log.New("test"),

@ -13,7 +13,7 @@ import (
"golang.org/x/oauth2/google"
)
// ApplyRoute should use the plugin route data to set auth headers and custom headers
// ApplyRoute should use the plugin route data to set auth headers and custom headers.
func ApplyRoute(ctx context.Context, req *http.Request, proxyPath string, route *plugins.AppPluginRoute, ds *models.DataSource) {
proxyPath = strings.TrimPrefix(proxyPath, route.Path)

File diff suppressed because it is too large Load Diff

@ -4,6 +4,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/setting"
macaron "gopkg.in/macaron.v1"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
@ -12,9 +13,8 @@ import (
"net/http"
"github.com/grafana/grafana/pkg/infra/log"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
macaron "gopkg.in/macaron.v1"
"github.com/stretchr/testify/require"
)
type testLogger struct {
@ -28,8 +28,8 @@ func (stub *testLogger) Warn(testMessage string, ctx ...interface{}) {
stub.warnMessage = testMessage
}
func TestTeamApiEndpoint(t *testing.T) {
Convey("Given two teams", t, func() {
func TestTeamAPIEndpoint(t *testing.T) {
t.Run("Given two teams", func(t *testing.T) {
mockResult := models.SearchTeamQueryResult{
Teams: []*models.TeamDTO{
{Name: "team1"},
@ -42,56 +42,52 @@ func TestTeamApiEndpoint(t *testing.T) {
Cfg: setting.NewCfg(),
}
Convey("When searching with no parameters", func() {
loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
query.Result = mockResult
loggedInUserScenario(t, "When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
query.Result = mockResult
sentLimit = query.Limit
sendPage = query.Page
sentLimit = query.Limit
sendPage = query.Page
return nil
})
return nil
})
sc.handlerFunc = hs.SearchTeams
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
sc.handlerFunc = hs.SearchTeams
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sentLimit, ShouldEqual, 1000)
So(sendPage, ShouldEqual, 1)
assert.Equal(t, 1000, sentLimit)
assert.Equal(t, 1, sendPage)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
So(respJSON.Get("totalCount").MustInt(), ShouldEqual, 2)
So(len(respJSON.Get("teams").MustArray()), ShouldEqual, 2)
})
assert.Equal(t, 2, respJSON.Get("totalCount").MustInt())
assert.Equal(t, 2, len(respJSON.Get("teams").MustArray()))
})
Convey("When searching with page and perpage parameters", func() {
loggedInUserScenario("When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
query.Result = mockResult
loggedInUserScenario(t, "When calling GET on", "/api/teams/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchTeamsQuery) error {
query.Result = mockResult
sentLimit = query.Limit
sendPage = query.Page
sentLimit = query.Limit
sendPage = query.Page
return nil
})
return nil
})
sc.handlerFunc = hs.SearchTeams
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
sc.handlerFunc = hs.SearchTeams
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
So(sentLimit, ShouldEqual, 10)
So(sendPage, ShouldEqual, 2)
})
assert.Equal(t, 10, sentLimit)
assert.Equal(t, 2, sendPage)
})
})
t.Run("When creating team with api key", func(t *testing.T) {
t.Run("When creating team with API key", func(t *testing.T) {
defer bus.ClearBusHandlers()
hs := &HTTPServer{

@ -10,50 +10,49 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestUserApiEndpoint(t *testing.T) {
Convey("Given a user is logged in", t, func() {
mockResult := models.SearchUserQueryResult{
Users: []*models.UserSearchHitDTO{
{Name: "user1"},
{Name: "user2"},
},
TotalCount: 2,
}
loggedInUserScenario("When calling GET on", "api/users/:id", func(sc *scenarioContext) {
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
bus.AddHandler("test", func(query *models.GetUserProfileQuery) error {
query.Result = models.UserProfileDTO{
Id: int64(1),
Email: "daniel@grafana.com",
Name: "Daniel",
Login: "danlee",
OrgId: int64(2),
IsGrafanaAdmin: true,
IsDisabled: false,
IsExternal: false,
UpdatedAt: fakeNow,
CreatedAt: fakeNow,
}
return nil
})
bus.AddHandler("test", func(query *models.GetAuthInfoQuery) error {
query.Result = &models.UserAuth{
AuthModule: models.AuthModuleLDAP,
}
return nil
})
sc.handlerFunc = GetUserByID
avatarUrl := dtos.GetGravatarUrl("daniel@grafana.com")
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
expected := fmt.Sprintf(`
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
mockResult := models.SearchUserQueryResult{
Users: []*models.UserSearchHitDTO{
{Name: "user1"},
{Name: "user2"},
},
TotalCount: 2,
}
loggedInUserScenario(t, "When calling GET on", "api/users/:id", func(sc *scenarioContext) {
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
bus.AddHandler("test", func(query *models.GetUserProfileQuery) error {
query.Result = models.UserProfileDTO{
Id: int64(1),
Email: "daniel@grafana.com",
Name: "Daniel",
Login: "danlee",
OrgId: int64(2),
IsGrafanaAdmin: true,
IsDisabled: false,
IsExternal: false,
UpdatedAt: fakeNow,
CreatedAt: fakeNow,
}
return nil
})
bus.AddHandler("test", func(query *models.GetAuthInfoQuery) error {
query.Result = &models.UserAuth{
AuthModule: models.AuthModuleLDAP,
}
return nil
})
sc.handlerFunc = GetUserByID
avatarUrl := dtos.GetGravatarUrl("daniel@grafana.com")
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
expected := fmt.Sprintf(`
{
"id": 1,
"email": "daniel@grafana.com",
@ -73,35 +72,35 @@ func TestUserApiEndpoint(t *testing.T) {
}
`, avatarUrl)
require.Equal(t, http.StatusOK, sc.resp.Code)
require.JSONEq(t, expected, sc.resp.Body.String())
require.Equal(t, http.StatusOK, sc.resp.Code)
require.JSONEq(t, expected, sc.resp.Body.String())
})
loggedInUserScenario(t, "When calling GET on", "/api/users/lookup", func(sc *scenarioContext) {
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
bus.AddHandler("test", func(query *models.GetUserByLoginQuery) error {
require.Equal(t, "danlee", query.LoginOrEmail)
query.Result = &models.User{
Id: int64(1),
Email: "daniel@grafana.com",
Name: "Daniel",
Login: "danlee",
Theme: "light",
IsAdmin: true,
OrgId: int64(2),
IsDisabled: false,
Updated: fakeNow,
Created: fakeNow,
}
return nil
})
loggedInUserScenario("When calling GET on", "/api/users/lookup", func(sc *scenarioContext) {
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
bus.AddHandler("test", func(query *models.GetUserByLoginQuery) error {
require.Equal(t, "danlee", query.LoginOrEmail)
query.Result = &models.User{
Id: int64(1),
Email: "daniel@grafana.com",
Name: "Daniel",
Login: "danlee",
Theme: "light",
IsAdmin: true,
OrgId: int64(2),
IsDisabled: false,
Updated: fakeNow,
Created: fakeNow,
}
return nil
})
sc.handlerFunc = GetUserByLoginOrEmail
sc.fakeReqWithParams("GET", sc.url, map[string]string{"loginOrEmail": "danlee"}).exec()
expected := `
sc.handlerFunc = GetUserByLoginOrEmail
sc.fakeReqWithParams("GET", sc.url, map[string]string{"loginOrEmail": "danlee"}).exec()
expected := `
{
"id": 1,
"email": "daniel@grafana.com",
@ -119,94 +118,93 @@ func TestUserApiEndpoint(t *testing.T) {
}
`
require.Equal(t, http.StatusOK, sc.resp.Code)
require.JSONEq(t, expected, sc.resp.Body.String())
require.Equal(t, http.StatusOK, sc.resp.Code)
require.JSONEq(t, expected, sc.resp.Body.String())
})
loggedInUserScenario(t, "When calling GET on", "/api/users", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
sentLimit = query.Limit
sendPage = query.Page
return nil
})
loggedInUserScenario("When calling GET on", "/api/users", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
sc.handlerFunc = SearchUsers
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
sentLimit = query.Limit
sendPage = query.Page
assert.Equal(t, 1000, sentLimit)
assert.Equal(t, 1, sendPage)
return nil
})
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
assert.Equal(t, 2, len(respJSON.MustArray()))
})
sc.handlerFunc = SearchUsers
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
loggedInUserScenario(t, "When calling GET with page and limit querystring parameters on", "/api/users", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
So(sentLimit, ShouldEqual, 1000)
So(sendPage, ShouldEqual, 1)
sentLimit = query.Limit
sendPage = query.Page
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
So(len(respJSON.MustArray()), ShouldEqual, 2)
return nil
})
loggedInUserScenario("When calling GET with page and limit querystring parameters on", "/api/users", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
sc.handlerFunc = SearchUsers
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
sentLimit = query.Limit
sendPage = query.Page
assert.Equal(t, 10, sentLimit)
assert.Equal(t, 2, sendPage)
})
return nil
})
loggedInUserScenario(t, "When calling GET on", "/api/users/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
sc.handlerFunc = SearchUsers
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
sentLimit = query.Limit
sendPage = query.Page
So(sentLimit, ShouldEqual, 10)
So(sendPage, ShouldEqual, 2)
return nil
})
loggedInUserScenario("When calling GET on", "/api/users/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
sc.handlerFunc = SearchUsersWithPaging
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
sentLimit = query.Limit
sendPage = query.Page
assert.Equal(t, 1000, sentLimit)
assert.Equal(t, 1, sendPage)
return nil
})
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
require.NoError(t, err)
sc.handlerFunc = SearchUsersWithPaging
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
assert.Equal(t, 2, respJSON.Get("totalCount").MustInt())
assert.Equal(t, 2, len(respJSON.Get("users").MustArray()))
})
So(sentLimit, ShouldEqual, 1000)
So(sendPage, ShouldEqual, 1)
loggedInUserScenario(t, "When calling GET with page and perpage querystring parameters on", "/api/users/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
So(err, ShouldBeNil)
sentLimit = query.Limit
sendPage = query.Page
So(respJSON.Get("totalCount").MustInt(), ShouldEqual, 2)
So(len(respJSON.Get("users").MustArray()), ShouldEqual, 2)
return nil
})
loggedInUserScenario("When calling GET with page and perpage querystring parameters on", "/api/users/search", func(sc *scenarioContext) {
var sentLimit int
var sendPage int
bus.AddHandler("test", func(query *models.SearchUsersQuery) error {
query.Result = mockResult
sentLimit = query.Limit
sendPage = query.Page
return nil
})
sc.handlerFunc = SearchUsersWithPaging
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
sc.handlerFunc = SearchUsersWithPaging
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
So(sentLimit, ShouldEqual, 10)
So(sendPage, ShouldEqual, 2)
})
assert.Equal(t, 10, sentLimit)
assert.Equal(t, 2, sendPage)
})
}

@ -2,115 +2,117 @@ package api
import (
"context"
"fmt"
"testing"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
)
func TestUserTokenApiEndpoint(t *testing.T) {
Convey("When current user attempts to revoke an auth token for a non-existing user", t, func() {
userId := int64(0)
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userId = cmd.Id
return models.ErrUserNotFound
})
func TestUserTokenAPIEndpoint(t *testing.T) {
t.Run("When current user attempts to revoke an auth token for a non-existing user", func(t *testing.T) {
cmd := models.RevokeAuthTokenCmd{AuthTokenId: 2}
revokeUserAuthTokenScenario("Should return not found when calling POST on", "/api/user/revoke-auth-token", "/api/user/revoke-auth-token", cmd, 200, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
So(userId, ShouldEqual, 200)
})
revokeUserAuthTokenScenario(t, "Should return not found when calling POST on", "/api/user/revoke-auth-token",
"/api/user/revoke-auth-token", cmd, 200, func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userID = cmd.Id
return models.ErrUserNotFound
})
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
assert.Equal(t, 404, sc.resp.Code)
assert.Equal(t, int64(200), userID)
})
})
Convey("When current user gets auth tokens for a non-existing user", t, func() {
userId := int64(0)
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userId = cmd.Id
return models.ErrUserNotFound
})
t.Run("When current user gets auth tokens for a non-existing user", func(t *testing.T) {
getUserAuthTokensScenario(t, "Should return not found when calling GET on", "/api/user/auth-tokens", "/api/user/auth-tokens", 200, func(sc *scenarioContext) {
var userID int64
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
userID = cmd.Id
return models.ErrUserNotFound
})
getUserAuthTokensScenario("Should return not found when calling GET on", "/api/user/auth-tokens", "/api/user/auth-tokens", 200, func(sc *scenarioContext) {
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
So(userId, ShouldEqual, 200)
assert.Equal(t, 404, sc.resp.Code)
assert.Equal(t, int64(200), userID)
})
})
Convey("When logout an existing user from all devices", t, func() {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: 200}
return nil
})
t.Run("When logging out an existing user from all devices", func(t *testing.T) {
logoutUserFromAllDevicesInternalScenario(t, "Should be successful", 1, func(sc *scenarioContext) {
const userID int64 = 200
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: userID}
return nil
})
logoutUserFromAllDevicesInternalScenario("Should be successful", 1, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
})
Convey("When logout a non-existing user from all devices", t, func() {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
return models.ErrUserNotFound
})
t.Run("When logout a non-existing user from all devices", func(t *testing.T) {
logoutUserFromAllDevicesInternalScenario(t, "Should return not found", testUserID, func(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
return models.ErrUserNotFound
})
logoutUserFromAllDevicesInternalScenario("Should return not found", TestUserID, func(sc *scenarioContext) {
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 404)
assert.Equal(t, 404, sc.resp.Code)
})
})
Convey("When revoke an auth token for a user", t, func() {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: 200}
return nil
})
t.Run("When revoke an auth token for a user", func(t *testing.T) {
cmd := models.RevokeAuthTokenCmd{AuthTokenId: 2}
token := &models.UserToken{Id: 1}
revokeUserAuthTokenInternalScenario("Should be successful", cmd, 200, token, func(sc *scenarioContext) {
revokeUserAuthTokenInternalScenario(t, "Should be successful", cmd, 200, token, func(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: 200}
return nil
})
sc.userAuthTokenService.GetUserTokenProvider = func(ctx context.Context, userId, userTokenId int64) (*models.UserToken, error) {
return &models.UserToken{Id: 2}, nil
}
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
})
})
Convey("When revoke the active auth token used by himself", t, func() {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: TestUserID}
return nil
})
t.Run("When revoke the active auth token used by himself", func(t *testing.T) {
cmd := models.RevokeAuthTokenCmd{AuthTokenId: 2}
token := &models.UserToken{Id: 2}
revokeUserAuthTokenInternalScenario("Should not be successful", cmd, TestUserID, token, func(sc *scenarioContext) {
revokeUserAuthTokenInternalScenario(t, "Should not be successful", cmd, testUserID, token, func(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: testUserID}
return nil
})
sc.userAuthTokenService.GetUserTokenProvider = func(ctx context.Context, userId, userTokenId int64) (*models.UserToken, error) {
return token, nil
}
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 400)
assert.Equal(t, 400, sc.resp.Code)
})
})
Convey("When gets auth tokens for a user", t, func() {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: TestUserID}
return nil
})
t.Run("When gets auth tokens for a user", func(t *testing.T) {
currentToken := &models.UserToken{Id: 1}
getUserAuthTokensInternalScenario("Should be successful", currentToken, func(sc *scenarioContext) {
getUserAuthTokensInternalScenario(t, "Should be successful", currentToken, func(sc *scenarioContext) {
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
cmd.Result = &models.User{Id: testUserID}
return nil
})
tokens := []*models.UserToken{
{
Id: 1,
@ -132,42 +134,43 @@ func TestUserTokenApiEndpoint(t *testing.T) {
}
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
So(sc.resp.Code, ShouldEqual, 200)
assert.Equal(t, 200, sc.resp.Code)
result := sc.ToJSON()
So(result.MustArray(), ShouldHaveLength, 2)
assert.Len(t, result.MustArray(), 2)
resultOne := result.GetIndex(0)
So(resultOne.Get("id").MustInt64(), ShouldEqual, tokens[0].Id)
So(resultOne.Get("isActive").MustBool(), ShouldBeTrue)
So(resultOne.Get("clientIp").MustString(), ShouldEqual, "127.0.0.1")
So(resultOne.Get("createdAt").MustString(), ShouldEqual, time.Unix(tokens[0].CreatedAt, 0).Format(time.RFC3339))
So(resultOne.Get("seenAt").MustString(), ShouldEqual, time.Unix(tokens[0].SeenAt, 0).Format(time.RFC3339))
So(resultOne.Get("device").MustString(), ShouldEqual, "Other")
So(resultOne.Get("browser").MustString(), ShouldEqual, "Chrome")
So(resultOne.Get("browserVersion").MustString(), ShouldEqual, "72.0")
So(resultOne.Get("os").MustString(), ShouldEqual, "Linux")
So(resultOne.Get("osVersion").MustString(), ShouldEqual, "")
assert.Equal(t, tokens[0].Id, resultOne.Get("id").MustInt64())
assert.True(t, resultOne.Get("isActive").MustBool())
assert.Equal(t, "127.0.0.1", resultOne.Get("clientIp").MustString())
assert.Equal(t, time.Unix(tokens[0].CreatedAt, 0).Format(time.RFC3339), resultOne.Get("createdAt").MustString())
assert.Equal(t, time.Unix(tokens[0].SeenAt, 0).Format(time.RFC3339), resultOne.Get("seenAt").MustString())
assert.Equal(t, "Other", resultOne.Get("device").MustString())
assert.Equal(t, "Chrome", resultOne.Get("browser").MustString())
assert.Equal(t, "72.0", resultOne.Get("browserVersion").MustString())
assert.Equal(t, "Linux", resultOne.Get("os").MustString())
assert.Empty(t, resultOne.Get("osVersion").MustString())
resultTwo := result.GetIndex(1)
So(resultTwo.Get("id").MustInt64(), ShouldEqual, tokens[1].Id)
So(resultTwo.Get("isActive").MustBool(), ShouldBeFalse)
So(resultTwo.Get("clientIp").MustString(), ShouldEqual, "127.0.0.2")
So(resultTwo.Get("createdAt").MustString(), ShouldEqual, time.Unix(tokens[1].CreatedAt, 0).Format(time.RFC3339))
So(resultTwo.Get("seenAt").MustString(), ShouldEqual, time.Unix(tokens[1].CreatedAt, 0).Format(time.RFC3339))
So(resultTwo.Get("device").MustString(), ShouldEqual, "iPhone")
So(resultTwo.Get("browser").MustString(), ShouldEqual, "Mobile Safari")
So(resultTwo.Get("browserVersion").MustString(), ShouldEqual, "11.0")
So(resultTwo.Get("os").MustString(), ShouldEqual, "iOS")
So(resultTwo.Get("osVersion").MustString(), ShouldEqual, "11.0")
assert.Equal(t, tokens[1].Id, resultTwo.Get("id").MustInt64())
assert.False(t, resultTwo.Get("isActive").MustBool())
assert.Equal(t, "127.0.0.2", resultTwo.Get("clientIp").MustString())
assert.Equal(t, time.Unix(tokens[1].CreatedAt, 0).Format(time.RFC3339), resultTwo.Get("createdAt").MustString())
assert.Equal(t, time.Unix(tokens[1].CreatedAt, 0).Format(time.RFC3339), resultTwo.Get("seenAt").MustString())
assert.Equal(t, "iPhone", resultTwo.Get("device").MustString())
assert.Equal(t, "Mobile Safari", resultTwo.Get("browser").MustString())
assert.Equal(t, "11.0", resultTwo.Get("browserVersion").MustString())
assert.Equal(t, "iOS", resultTwo.Get("os").MustString())
assert.Equal(t, "11.0", resultTwo.Get("osVersion").MustString())
})
})
}
func revokeUserAuthTokenScenario(desc string, url string, routePattern string, cmd models.RevokeAuthTokenCmd, userId int64, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func revokeUserAuthTokenScenario(t *testing.T, desc string, url string, routePattern string, cmd models.RevokeAuthTokenCmd,
userId int64, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -176,12 +179,12 @@ func revokeUserAuthTokenScenario(desc string, url string, routePattern string, c
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = userId
sc.context.OrgId = TestOrgID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.RevokeUserAuthToken(c, cmd)
@ -193,9 +196,9 @@ func revokeUserAuthTokenScenario(desc string, url string, routePattern string, c
})
}
func getUserAuthTokensScenario(desc string, url string, routePattern string, userId int64, fn scenarioFunc) {
Convey(desc+" "+url, func() {
defer bus.ClearBusHandlers()
func getUserAuthTokensScenario(t *testing.T, desc string, url string, routePattern string, userId int64, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -204,12 +207,12 @@ func getUserAuthTokensScenario(desc string, url string, routePattern string, use
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext(url)
sc := setupScenarioContext(t, url)
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = userId
sc.context.OrgId = TestOrgID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.GetUserAuthTokens(c)
@ -221,20 +224,20 @@ func getUserAuthTokensScenario(desc string, url string, routePattern string, use
})
}
func logoutUserFromAllDevicesInternalScenario(desc string, userId int64, fn scenarioFunc) {
Convey(desc, func() {
defer bus.ClearBusHandlers()
func logoutUserFromAllDevicesInternalScenario(t *testing.T, desc string, userId int64, fn scenarioFunc) {
t.Run(desc, func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
hs := HTTPServer{
Bus: bus.GetBus(),
AuthTokenService: auth.NewFakeUserAuthTokenService(),
}
sc := setupScenarioContext("/")
sc := setupScenarioContext(t, "/")
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
return hs.logoutUserFromAllDevicesInternal(context.Background(), userId)
@ -246,9 +249,10 @@ func logoutUserFromAllDevicesInternalScenario(desc string, userId int64, fn scen
})
}
func revokeUserAuthTokenInternalScenario(desc string, cmd models.RevokeAuthTokenCmd, userId int64, token *models.UserToken, fn scenarioFunc) {
Convey(desc, func() {
defer bus.ClearBusHandlers()
func revokeUserAuthTokenInternalScenario(t *testing.T, desc string, cmd models.RevokeAuthTokenCmd, userId int64,
token *models.UserToken, fn scenarioFunc) {
t.Run(desc, func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -257,12 +261,12 @@ func revokeUserAuthTokenInternalScenario(desc string, cmd models.RevokeAuthToken
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext("/")
sc := setupScenarioContext(t, "/")
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
sc.context.UserToken = token
@ -275,9 +279,9 @@ func revokeUserAuthTokenInternalScenario(desc string, cmd models.RevokeAuthToken
})
}
func getUserAuthTokensInternalScenario(desc string, token *models.UserToken, fn scenarioFunc) {
Convey(desc, func() {
defer bus.ClearBusHandlers()
func getUserAuthTokensInternalScenario(t *testing.T, desc string, token *models.UserToken, fn scenarioFunc) {
t.Run(desc, func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
@ -286,16 +290,16 @@ func getUserAuthTokensInternalScenario(desc string, token *models.UserToken, fn
AuthTokenService: fakeAuthTokenService,
}
sc := setupScenarioContext("/")
sc := setupScenarioContext(t, "/")
sc.userAuthTokenService = fakeAuthTokenService
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
sc.context = c
sc.context.UserId = TestUserID
sc.context.OrgId = TestOrgID
sc.context.UserId = testUserID
sc.context.OrgId = testOrgID
sc.context.OrgRole = models.ROLE_ADMIN
sc.context.UserToken = token
return hs.getUserAuthTokensInternal(c, TestUserID)
return hs.getUserAuthTokensInternal(c, testUserID)
})
sc.m.Get("/", sc.defaultHandler)

@ -34,7 +34,7 @@ func (*OSSLicensingService) StateInfo() string {
func (l *OSSLicensingService) LicenseURL(user *models.SignedInUser) string {
if user.IsGrafanaAdmin {
return l.Cfg.AppSubUrl + "/admin/upgrading"
return l.Cfg.AppSubURL + "/admin/upgrading"
}
return "https://grafana.com/products/enterprise/?utm_source=grafana_footer"

@ -235,7 +235,7 @@ func (rs *RenderingService) getURL(path string) string {
subPath := ""
if rs.Cfg.ServeFromSubPath {
subPath = rs.Cfg.AppSubUrl
subPath = rs.Cfg.AppSubURL
}
// &render=1 signals to the legacy redirect layer to

@ -27,14 +27,14 @@ func TestGetUrl(t *testing.T) {
t.Run("And protocol HTTP configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false
rs.Cfg.AppSubUrl = ""
rs.Cfg.AppSubURL = ""
setting.Protocol = setting.HTTPScheme
url := rs.getURL(path)
require.Equal(t, "http://localhost:3000/"+path+"&render=1", url)
t.Run("And serve from sub path should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = true
rs.Cfg.AppSubUrl = "/grafana"
rs.Cfg.AppSubURL = "/grafana"
url := rs.getURL(path)
require.Equal(t, "http://localhost:3000/grafana/"+path+"&render=1", url)
})
@ -42,7 +42,7 @@ func TestGetUrl(t *testing.T) {
t.Run("And protocol HTTPS configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false
rs.Cfg.AppSubUrl = ""
rs.Cfg.AppSubURL = ""
setting.Protocol = setting.HTTPSScheme
url := rs.getURL(path)
require.Equal(t, "https://localhost:3000/"+path+"&render=1", url)
@ -50,7 +50,7 @@ func TestGetUrl(t *testing.T) {
t.Run("And protocol HTTP2 configured should return expected path", func(t *testing.T) {
rs.Cfg.ServeFromSubPath = false
rs.Cfg.AppSubUrl = ""
rs.Cfg.AppSubURL = ""
setting.Protocol = setting.HTTP2Scheme
url := rs.getURL(path)
require.Equal(t, "https://localhost:3000/"+path+"&render=1", url)

@ -78,7 +78,7 @@ var (
// Log settings.
LogConfigs []util.DynMap
// Http server options
// HTTP server options
Protocol Scheme
Domain string
HttpAddr, HttpPort string
@ -193,7 +193,7 @@ var (
LDAPAllowSignup bool
LDAPActiveSyncEnabled bool
// QUOTA
// Quota
Quota QuotaSettings
// Alerting
@ -228,8 +228,8 @@ type Cfg struct {
Logger log.Logger
// HTTP Server Settings
AppUrl string
AppSubUrl string
AppURL string
AppSubURL string
ServeFromSubPath bool
StaticRootPath string
Protocol Scheme
@ -707,7 +707,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
cfg.Raw = iniFile
// Temporary keep global, to make refactor in steps
// Temporarily keep global, to make refactor in steps
Raw = cfg.Raw
cfg.BuildVersion = BuildVersion
@ -1203,8 +1203,8 @@ func readServerSettings(iniFile *ini.File, cfg *Cfg) error {
}
ServeFromSubPath = server.Key("serve_from_sub_path").MustBool(false)
cfg.AppUrl = AppUrl
cfg.AppSubUrl = AppSubUrl
cfg.AppURL = AppUrl
cfg.AppSubURL = AppSubUrl
cfg.ServeFromSubPath = ServeFromSubPath
Protocol = HTTPScheme

Loading…
Cancel
Save