mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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>
This commit is contained in:
@@ -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,
|
||||
}
|
||||
|
||||
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)
|
||||
assert.Equal(t, 400, sc.resp.Code)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a server admin attempts to logout himself from all devices", t, func() {
|
||||
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}
|
||||
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)
|
||||
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)
|
||||
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)
|
||||
|
||||
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
|
||||
userId = cmd.Id
|
||||
userID = cmd.Id
|
||||
return models.ErrUserNotFound
|
||||
})
|
||||
|
||||
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)
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a server admin gets auth tokens for a non-existing user", t, func() {
|
||||
userId := int64(0)
|
||||
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
|
||||
userID = cmd.Id
|
||||
return models.ErrUserNotFound
|
||||
})
|
||||
|
||||
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)
|
||||
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 enable/disable a nonexistent user", t, func() {
|
||||
var userId int64
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
userID = cmd.UserId
|
||||
isDisabled = cmd.IsDisabled
|
||||
return models.ErrUserNotFound
|
||||
})
|
||||
|
||||
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()
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "user not found")
|
||||
assert.Equal(t, "user not found", respJSON.Get("message").MustString())
|
||||
|
||||
So(userId, ShouldEqual, 42)
|
||||
So(isDisabled, ShouldEqual, false)
|
||||
assert.Equal(t, int64(42), userID)
|
||||
assert.Equal(t, false, isDisabled)
|
||||
})
|
||||
|
||||
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()
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "user not found")
|
||||
|
||||
So(userId, ShouldEqual, 42)
|
||||
So(isDisabled, ShouldEqual, true)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When a server admin attempts to disable/enable external user", t, func() {
|
||||
userId := int64(0)
|
||||
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 {
|
||||
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)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "Could not disable external user")
|
||||
|
||||
So(userId, ShouldEqual, 42)
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
So(respJSON.Get("message").MustString(), ShouldEqual, "Could not enable external user")
|
||||
|
||||
So(userId, ShouldEqual, 42)
|
||||
})
|
||||
})
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
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()
|
||||
bus.AddHandler("test", func(cmd *models.DisableUserCommand) error {
|
||||
userID = cmd.UserId
|
||||
isDisabled = cmd.IsDisabled
|
||||
return models.ErrUserNotFound
|
||||
})
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
sc.fakeReqWithParams("POST", sc.url, map[string]string{}).exec()
|
||||
|
||||
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")
|
||||
require.NoError(t, err)
|
||||
|
||||
So(userId, ShouldEqual, 42)
|
||||
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 create a user", t, func() {
|
||||
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}
|
||||
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
|
||||
})
|
||||
|
||||
Convey("Without an organization", func() {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: TestLogin,
|
||||
Password: TestPassword,
|
||||
}
|
||||
|
||||
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, 500, 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, "Could not disable external user", respJSON.Get("message").MustString())
|
||||
|
||||
// test that userLogin and orgId were transmitted correctly to the handler
|
||||
So(userLogin, ShouldEqual, TestLogin)
|
||||
So(orgId, ShouldEqual, 0)
|
||||
})
|
||||
assert.Equal(t, int64(42), userID)
|
||||
})
|
||||
|
||||
Convey("With an organization", func() {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: TestLogin,
|
||||
Password: TestPassword,
|
||||
OrgId: TestOrgID,
|
||||
}
|
||||
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
|
||||
})
|
||||
|
||||
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, 500, 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, "Could not enable external user", respJSON.Get("message").MustString())
|
||||
|
||||
So(userLogin, ShouldEqual, TestLogin)
|
||||
So(orgId, ShouldEqual, TestOrgID)
|
||||
assert.Equal(t, int64(42), userID)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("With a nonexistent organization", func() {
|
||||
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
|
||||
})
|
||||
|
||||
sc.fakeReqWithParams("DELETE", 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)
|
||||
assert.Equal(t, "user not found", respJSON.Get("message").MustString())
|
||||
|
||||
assert.Equal(t, int64(42), userID)
|
||||
})
|
||||
})
|
||||
|
||||
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,
|
||||
Login: testLogin,
|
||||
Password: testPassword,
|
||||
}
|
||||
|
||||
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()
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testUserID, respJSON.Get("id").MustInt64())
|
||||
assert.Equal(t, "User created", respJSON.Get("message").MustString())
|
||||
|
||||
// Verify that userLogin and orgID were transmitted correctly to the handler
|
||||
assert.Equal(t, testLogin, userLogin)
|
||||
assert.Equal(t, int64(0), orgID)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("With an organization", func(t *testing.T) {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: testLogin,
|
||||
Password: testPassword,
|
||||
OrgId: testOrgID,
|
||||
}
|
||||
|
||||
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()
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, testUserID, respJSON.Get("id").MustInt64())
|
||||
assert.Equal(t, "User created", respJSON.Get("message").MustString())
|
||||
|
||||
assert.Equal(t, testLogin, userLogin)
|
||||
assert.Equal(t, testOrgID, orgID)
|
||||
})
|
||||
})
|
||||
|
||||
t.Run("With a nonexistent organization", func(t *testing.T) {
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
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() {
|
||||
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,
|
||||
}
|
||||
|
||||
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
|
||||
})
|
||||
|
||||
createCmd := dtos.AdminCreateUserForm{
|
||||
Login: TestLogin,
|
||||
Password: TestPassword,
|
||||
}
|
||||
|
||||
adminCreateUserScenario("Should return an error", "/api/admin/users", "/api/admin/users", createCmd, func(sc *scenarioContext) {
|
||||
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() {
|
||||
t.Run("When user is editor and not in the ACL", func(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
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{
|
||||
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},
|
||||
}
|
||||
|
||||
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)
|
||||
},
|
||||
})
|
||||
|
||||
callPauseAlert(sc)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
})
|
||||
})
|
||||
|
||||
loggedInUserScenarioWithRole("When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
loggedInUserScenarioWithRole(t, "When calling GET on", "GET", "/api/alerts?dashboardId=1", "/api/alerts",
|
||||
models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
|
||||
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")
|
||||
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)
|
||||
|
||||
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, 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) {
|
||||
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()
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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()
|
||||
So(sc.resp.Code, ShouldEqual, 403)
|
||||
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) {
|
||||
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) {
|
||||
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) {
|
||||
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()
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
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,6 +130,7 @@ func TestAnnotationsApiEndpoint(t *testing.T) {
|
||||
{Role: &editorRole, Permission: models.PERMISSION_EDIT},
|
||||
}
|
||||
|
||||
setUp := func() {
|
||||
bus.AddHandler("test", func(query *models.GetDashboardAclInfoListQuery) error {
|
||||
query.Result = aclMockResp
|
||||
return nil
|
||||
@@ -129,80 +140,100 @@ func TestAnnotationsApiEndpoint(t *testing.T) {
|
||||
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) {
|
||||
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()
|
||||
So(sc.resp.Code, ShouldEqual, 403)
|
||||
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) {
|
||||
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()
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
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) {
|
||||
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()
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
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,20 +9,24 @@ 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() {
|
||||
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) {
|
||||
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)
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
})
|
||||
|
||||
cmd := dtos.UpdateDashboardAclCommand{
|
||||
@@ -30,25 +35,36 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
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")
|
||||
|
||||
setUp := func() {
|
||||
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) {
|
||||
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)
|
||||
So(sc.resp.Code, ShouldEqual, 403)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
})
|
||||
|
||||
cmd := dtos.UpdateDashboardAclCommand{
|
||||
@@ -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) {
|
||||
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 403)
|
||||
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,20 +99,24 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
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) {
|
||||
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)
|
||||
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(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)
|
||||
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{
|
||||
@@ -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) {
|
||||
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
})
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
t.Run("When trying to update permissions with duplicate permissions", func(t *testing.T) {
|
||||
origNewGuardian := guardian.New
|
||||
t.Cleanup(func() {
|
||||
guardian.New = origNewGuardian
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When trying to update permissions with duplicate permissions", func() {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||
CanAdminValue: true,
|
||||
CheckPermissionBeforeUpdateValue: false,
|
||||
CheckPermissionBeforeUpdateError: guardian.ErrGuardianPermissionExists,
|
||||
})
|
||||
|
||||
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) {
|
||||
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 400)
|
||||
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},
|
||||
)
|
||||
|
||||
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,13 +192,11 @@ func TestDashboardPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateDashboardPermissionScenario("When calling POST on", "/api/dashboards/id/1/permissions", "/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateDashboardPermissionScenario(t, "When calling POST on", "/api/dashboards/id/1/permissions",
|
||||
"/api/dashboards/id/:id/permissions", cmd, func(sc *scenarioContext) {
|
||||
setUp()
|
||||
callUpdateDashboardPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 400)
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
guardian.New = origNewGuardian
|
||||
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,15 +67,15 @@ 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)
|
||||
}))
|
||||
return mockSnapshotResult
|
||||
}
|
||||
|
||||
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) {
|
||||
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)
|
||||
|
||||
var externalRequest *http.Request
|
||||
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
|
||||
externalRequest = req
|
||||
})
|
||||
@@ -72,15 +84,17 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = DeleteDashboardSnapshot
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 403)
|
||||
So(externalRequest, ShouldBeNil)
|
||||
})
|
||||
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) {
|
||||
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)
|
||||
|
||||
var externalRequest *http.Request
|
||||
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(200)
|
||||
externalRequest = req
|
||||
@@ -90,27 +104,29 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = DeleteDashboardSnapshotByDeleteKey
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{"deleteKey": "12345"}).exec()
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
require.Equal(t, 200, sc.resp.Code)
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
So(respJSON.Get("message").MustString(), ShouldStartWith, "Snapshot deleted")
|
||||
assert.True(t, strings.HasPrefix(respJSON.Get("message").MustString(), "Snapshot deleted"))
|
||||
|
||||
So(externalRequest.Method, ShouldEqual, http.MethodGet)
|
||||
So(fmt.Sprintf("http://%s", externalRequest.Host), ShouldEqual, ts.URL)
|
||||
So(externalRequest.URL.EscapedPath(), ShouldEqual, "/")
|
||||
})
|
||||
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() {
|
||||
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},
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
@@ -120,43 +136,45 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = DeleteDashboardSnapshot
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).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)
|
||||
require.NoError(t, err)
|
||||
|
||||
So(respJSON.Get("message").MustString(), ShouldStartWith, "Snapshot deleted")
|
||||
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, 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() {
|
||||
t.Run("When user is editor and creator of the snapshot", func(t *testing.T) {
|
||||
aclMockResp = []*models.DashboardAclInfoDTO{}
|
||||
mockSnapshotResult.UserId = TestUserID
|
||||
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)
|
||||
|
||||
mockSnapshotResult.UserId = testUserID
|
||||
mockSnapshotResult.External = false
|
||||
|
||||
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()
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
So(respJSON.Get("message").MustString(), ShouldStartWith, "Snapshot deleted")
|
||||
})
|
||||
assert.True(t, strings.HasPrefix(respJSON.Get("message").MustString(), "Snapshot deleted"))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When deleting an external snapshot", func() {
|
||||
t.Run("When deleting an external snapshot", func(t *testing.T) {
|
||||
aclMockResp = []*models.DashboardAclInfoDTO{}
|
||||
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) {
|
||||
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)
|
||||
@@ -166,30 +184,37 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = DeleteDashboardSnapshot
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
|
||||
|
||||
So(writeErr, ShouldBeNil)
|
||||
So(sc.resp.Code, ShouldEqual, 200)
|
||||
})
|
||||
require.NoError(t, writeErr)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
})
|
||||
|
||||
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) {
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
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) {
|
||||
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
|
||||
|
||||
ts := setupRemoteServer(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(404)
|
||||
})
|
||||
@@ -198,42 +223,43 @@ func TestDashboardSnapshotApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = DeleteDashboardSnapshot
|
||||
sc.fakeReqWithParams("DELETE", sc.url, map[string]string{"key": "12345"}).exec()
|
||||
|
||||
So(sc.resp.Code, ShouldEqual, 500)
|
||||
})
|
||||
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) {
|
||||
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)
|
||||
|
||||
sc.handlerFunc = GetDashboardSnapshot
|
||||
sc.fakeReqWithParams("GET", sc.url, map[string]string{"key": "12345"}).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)
|
||||
require.NoError(t, err)
|
||||
|
||||
dashboard := respJSON.Get("dashboard")
|
||||
id := dashboard.Get("id")
|
||||
|
||||
So(id.MustInt64(), ShouldEqual, 100)
|
||||
})
|
||||
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()
|
||||
|
||||
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("dashboard").Get("id").MustInt64(), ShouldEqual, dashboardId)
|
||||
})
|
||||
})
|
||||
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,22 +6,21 @@ 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) {
|
||||
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 {
|
||||
So(query.OrgId, ShouldEqual, TestOrgID)
|
||||
assert.Equal(t, testOrgID, query.OrgId)
|
||||
query.Result = []*models.DataSource{
|
||||
{Name: "mmm"},
|
||||
{Name: "ZZZ"},
|
||||
@@ -37,23 +36,19 @@ func TestDataSourcesProxy(t *testing.T) {
|
||||
|
||||
respJSON := []map[string]interface{}{}
|
||||
err := json.NewDecoder(sc.resp.Body).Decode(&respJSON)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
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")
|
||||
})
|
||||
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"])
|
||||
})
|
||||
|
||||
Convey("Should be able to save a data source", func() {
|
||||
loggedInUserScenario("When calling DELETE on non-existing", "/api/datasources/name/12345", func(sc *scenarioContext) {
|
||||
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()
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
})
|
||||
})
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -61,9 +56,7 @@ func TestDataSourcesProxy(t *testing.T) {
|
||||
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,23 +10,25 @@ 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() {
|
||||
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("When calling GET on", "GET", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", models.ROLE_EDITOR, func(sc *scenarioContext) {
|
||||
loggedInUserScenarioWithRole(t, "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)
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
})
|
||||
|
||||
cmd := dtos.UpdateDashboardAclCommand{
|
||||
@@ -34,18 +37,20 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 404)
|
||||
assert.Equal(t, 404, sc.resp.Code)
|
||||
})
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
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
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Given user has no admin permissions", func() {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{CanAdminValue: false})
|
||||
|
||||
mock := &fakeFolderService{
|
||||
@@ -56,12 +61,11 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
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) {
|
||||
loggedInUserScenarioWithRole(t, "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)
|
||||
assert.Equal(t, 403, sc.resp.Code)
|
||||
})
|
||||
|
||||
cmd := dtos.UpdateDashboardAclCommand{
|
||||
@@ -70,19 +74,20 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 403)
|
||||
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
|
||||
origNewFolderService := dashboards.NewFolderService
|
||||
t.Cleanup(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,
|
||||
@@ -103,17 +108,16 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
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) {
|
||||
loggedInUserScenarioWithRole(t, "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)
|
||||
assert.Equal(t, 200, sc.resp.Code)
|
||||
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)
|
||||
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{
|
||||
@@ -122,23 +126,24 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
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").MustInt(), ShouldEqual, 1)
|
||||
So(respJSON.Get("title").MustString(), ShouldEqual, "Folder")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 1, respJSON.Get("id").MustInt())
|
||||
assert.Equal(t, "Folder", respJSON.Get("title").MustString())
|
||||
})
|
||||
})
|
||||
|
||||
Reset(func() {
|
||||
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
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When trying to update permissions with duplicate permissions", func() {
|
||||
origNewGuardian := guardian.New
|
||||
guardian.MockDashboardGuardian(&guardian.FakeDashboardGuardian{
|
||||
CanAdminValue: true,
|
||||
CheckPermissionBeforeUpdateValue: false,
|
||||
@@ -153,7 +158,6 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
origNewFolderService := dashboards.NewFolderService
|
||||
mockFolderService(mock)
|
||||
|
||||
cmd := dtos.UpdateDashboardAclCommand{
|
||||
@@ -162,19 +166,20 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateFolderPermissionScenario(t, "When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
callUpdateFolderPermissions(sc)
|
||||
So(sc.resp.Code, ShouldEqual, 400)
|
||||
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
|
||||
origNewFolderService := dashboards.NewFolderService
|
||||
t.Cleanup(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,
|
||||
@@ -189,7 +194,6 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
origNewFolderService := dashboards.NewFolderService
|
||||
mockFolderService(mock)
|
||||
|
||||
cmd := dtos.UpdateDashboardAclCommand{
|
||||
@@ -198,15 +202,9 @@ func TestFolderPermissionApiEndpoint(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
updateFolderPermissionScenario("When calling POST on", "/api/folders/uid/permissions", "/api/folders/:uid/permissions", cmd, func(sc *scenarioContext) {
|
||||
updateFolderPermissionScenario(t, "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
|
||||
})
|
||||
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,13 +10,12 @@ 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() {
|
||||
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",
|
||||
@@ -26,21 +25,20 @@ func TestFoldersApiEndpoint(t *testing.T) {
|
||||
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")
|
||||
})
|
||||
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() {
|
||||
t.Run("Given incorrect requests for creating a folder", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Error error
|
||||
ExpectedStatusCode int
|
||||
@@ -66,16 +64,15 @@ func TestFoldersApiEndpoint(t *testing.T) {
|
||||
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() {
|
||||
t.Run("Given a correct request for updating a folder", func(t *testing.T) {
|
||||
cmd := models.UpdateFolderCommand{
|
||||
Title: "Folder upd",
|
||||
}
|
||||
@@ -84,21 +81,20 @@ func TestFoldersApiEndpoint(t *testing.T) {
|
||||
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")
|
||||
})
|
||||
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() {
|
||||
t.Run("Given incorrect requests for updating a folder", func(t *testing.T) {
|
||||
testCases := []struct {
|
||||
Error error
|
||||
ExpectedStatusCode int
|
||||
@@ -123,34 +119,33 @@ func TestFoldersApiEndpoint(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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,6 +411,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string) *scenarioConte
|
||||
}
|
||||
|
||||
func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
@@ -432,8 +442,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
|
||||
})
|
||||
|
||||
assert.Equal(t, http.StatusOK, sc.resp.Code)
|
||||
|
||||
@@ -447,6 +456,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
@@ -460,8 +470,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
|
||||
|
||||
return models.ErrUserNotFound
|
||||
})
|
||||
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
|
||||
})
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, sc.resp.Code)
|
||||
|
||||
@@ -475,6 +484,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
@@ -486,8 +496,8 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
||||
userSearchError = multildap.ErrDidNotFindUser
|
||||
|
||||
admin := setting.AdminUser
|
||||
t.Cleanup(func() { setting.AdminUser = admin })
|
||||
setting.AdminUser = "ldap-daniel"
|
||||
defer func() { setting.AdminUser = admin }()
|
||||
|
||||
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
|
||||
require.Equal(t, q.Id, int64(34))
|
||||
@@ -502,8 +512,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
|
||||
})
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
||||
|
||||
@@ -518,6 +527,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
|
||||
getLDAPConfig = func() (*ldap.Config, error) {
|
||||
return &ldap.Config{}, nil
|
||||
}
|
||||
@@ -551,8 +561,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
|
||||
assert.Equal(t, 34, cmd.UserId)
|
||||
return nil
|
||||
})
|
||||
|
||||
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34")
|
||||
})
|
||||
|
||||
assert.Equal(t, http.StatusBadRequest, sc.resp.Code)
|
||||
|
||||
|
@@ -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)
|
||||
|
||||
|
@@ -26,12 +26,10 @@ import (
|
||||
"github.com/grafana/grafana/pkg/plugins"
|
||||
"github.com/grafana/grafana/pkg/setting"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
. "github.com/smartystreets/goconvey/convey"
|
||||
)
|
||||
|
||||
func TestDSRouteRule(t *testing.T) {
|
||||
Convey("DataSourceProxy", t, func() {
|
||||
Convey("Plugin with routes", func() {
|
||||
func TestDataSourceProxy_routeRule(t *testing.T) {
|
||||
t.Run("Plugin with routes", func(t *testing.T) {
|
||||
plugin := &plugins.DataSourcePlugin{
|
||||
Routes: []*plugins.AppPluginRoute{
|
||||
{
|
||||
@@ -74,8 +72,14 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
origSecretKey := setting.SecretKey
|
||||
t.Cleanup(func() {
|
||||
setting.SecretKey = origSecretKey
|
||||
})
|
||||
setting.SecretKey = "password" //nolint:goconst
|
||||
key, _ := util.Encrypt([]byte("123"), "password")
|
||||
|
||||
key, err := util.Encrypt([]byte("123"), "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
@@ -88,75 +92,79 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
setUp := func() (*models.ReqContext, *http.Request) {
|
||||
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
require.NoError(t, err)
|
||||
ctx := &models.ReqContext{
|
||||
Context: &macaron.Context{
|
||||
Req: macaron.Request{Request: req},
|
||||
},
|
||||
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR},
|
||||
}
|
||||
return ctx, req
|
||||
}
|
||||
|
||||
Convey("When matching route path", func() {
|
||||
t.Run("When matching route path", func(t *testing.T) {
|
||||
ctx, req := setUp()
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/v4/some/method", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
proxy.route = plugin.Routes[0]
|
||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
|
||||
|
||||
Convey("should add headers and update url", func() {
|
||||
So(req.URL.String(), ShouldEqual, "https://www.google.com/some/method")
|
||||
So(req.Header.Get("x-header"), ShouldEqual, "my secret 123")
|
||||
})
|
||||
assert.Equal(t, "https://www.google.com/some/method", req.URL.String())
|
||||
assert.Equal(t, "my secret 123", req.Header.Get("x-header"))
|
||||
})
|
||||
|
||||
Convey("When matching route path and has dynamic url", func() {
|
||||
t.Run("When matching route path and has dynamic url", func(t *testing.T) {
|
||||
ctx, req := setUp()
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/common/some/method", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
proxy.route = plugin.Routes[3]
|
||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
|
||||
|
||||
Convey("should add headers and interpolate the url with query string parameters", func() {
|
||||
So(req.URL.String(), ShouldEqual, "https://dynamic.grafana.com/some/method?apiKey=123")
|
||||
So(req.Header.Get("x-header"), ShouldEqual, "my secret 123")
|
||||
})
|
||||
assert.Equal(t, "https://dynamic.grafana.com/some/method?apiKey=123", req.URL.String())
|
||||
assert.Equal(t, "my secret 123", req.Header.Get("x-header"))
|
||||
})
|
||||
|
||||
Convey("When matching route path with no url", func() {
|
||||
t.Run("When matching route path with no url", func(t *testing.T) {
|
||||
ctx, req := setUp()
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
proxy.route = plugin.Routes[4]
|
||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, proxy.route, proxy.ds)
|
||||
|
||||
Convey("Should not replace request url", func() {
|
||||
So(req.URL.String(), ShouldEqual, "http://localhost/asd")
|
||||
})
|
||||
assert.Equal(t, "http://localhost/asd", req.URL.String())
|
||||
})
|
||||
|
||||
Convey("Validating request", func() {
|
||||
Convey("plugin route with valid role", func() {
|
||||
t.Run("Validating request", func(t *testing.T) {
|
||||
t.Run("plugin route with valid role", func(t *testing.T) {
|
||||
ctx, _ := setUp()
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/v4/some/method", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
err = proxy.validateRequest()
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
|
||||
Convey("plugin route with admin role and user is editor", func() {
|
||||
t.Run("plugin route with admin role and user is editor", func(t *testing.T) {
|
||||
ctx, _ := setUp()
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/admin", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
err = proxy.validateRequest()
|
||||
So(err, ShouldNotBeNil)
|
||||
require.Error(t, err)
|
||||
})
|
||||
|
||||
Convey("plugin route with admin role and user is admin", func() {
|
||||
t.Run("plugin route with admin role and user is admin", func(t *testing.T) {
|
||||
ctx, _ := setUp()
|
||||
ctx.SignedInUser.OrgRole = models.ROLE_ADMIN
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "api/admin", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
err = proxy.validateRequest()
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("Plugin with multiple routes for token auth", func() {
|
||||
t.Run("Plugin with multiple routes for token auth", func(t *testing.T) {
|
||||
plugin := &plugins.DataSourcePlugin{
|
||||
Routes: []*plugins.AppPluginRoute{
|
||||
{
|
||||
@@ -188,8 +196,14 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
origSecretKey := setting.SecretKey
|
||||
t.Cleanup(func() {
|
||||
setting.SecretKey = origSecretKey
|
||||
})
|
||||
setting.SecretKey = "password"
|
||||
key, _ := util.Encrypt([]byte("123"), "password")
|
||||
|
||||
key, err := util.Encrypt([]byte("123"), "password")
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
JsonData: simplejson.NewFromAny(map[string]interface{}{
|
||||
@@ -201,7 +215,8 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
require.NoError(t, err)
|
||||
ctx := &models.ReqContext{
|
||||
Context: &macaron.Context{
|
||||
Req: macaron.Request{Request: req},
|
||||
@@ -209,83 +224,87 @@ func TestDSRouteRule(t *testing.T) {
|
||||
SignedInUser: &models.SignedInUser{OrgRole: models.ROLE_EDITOR},
|
||||
}
|
||||
|
||||
Convey("When creating and caching access tokens", func() {
|
||||
t.Run("When creating and caching access tokens", func(t *testing.T) {
|
||||
var authorizationHeaderCall1 string
|
||||
var authorizationHeaderCall2 string
|
||||
|
||||
Convey("first call should add authorization header with access token", func() {
|
||||
t.Run("first call should add authorization header with access token", func(t *testing.T) {
|
||||
json, err := ioutil.ReadFile("./test-data/access-token-1.json")
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
client = newFakeHTTPClient(json)
|
||||
proxy1, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
proxy1.route = plugin.Routes[0]
|
||||
ApplyRoute(proxy1.ctx.Req.Context(), req, proxy1.proxyPath, proxy1.route, proxy1.ds)
|
||||
client = newFakeHTTPClient(t, json)
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", &setting.Cfg{})
|
||||
require.NoError(t, err)
|
||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[0], proxy.ds)
|
||||
|
||||
authorizationHeaderCall1 = req.Header.Get("Authorization")
|
||||
So(req.URL.String(), ShouldEqual, "https://api.nr1.io/some/path")
|
||||
So(authorizationHeaderCall1, ShouldStartWith, "Bearer eyJ0e")
|
||||
assert.Equal(t, "https://api.nr1.io/some/path", req.URL.String())
|
||||
assert.True(t, strings.HasPrefix(authorizationHeaderCall1, "Bearer eyJ0e"))
|
||||
|
||||
Convey("second call to another route should add a different access token", func() {
|
||||
t.Run("second call to another route should add a different access token", func(t *testing.T) {
|
||||
json2, err := ioutil.ReadFile("./test-data/access-token-2.json")
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
client = newFakeHTTPClient(json2)
|
||||
proxy2, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken2", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
proxy2.route = plugin.Routes[1]
|
||||
ApplyRoute(proxy2.ctx.Req.Context(), req, proxy2.proxyPath, proxy2.route, proxy2.ds)
|
||||
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
require.NoError(t, err)
|
||||
client = newFakeHTTPClient(t, json2)
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken2", &setting.Cfg{})
|
||||
require.NoError(t, err)
|
||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[1], proxy.ds)
|
||||
|
||||
authorizationHeaderCall2 = req.Header.Get("Authorization")
|
||||
|
||||
So(req.URL.String(), ShouldEqual, "https://api.nr2.io/some/path")
|
||||
So(authorizationHeaderCall1, ShouldStartWith, "Bearer eyJ0e")
|
||||
So(authorizationHeaderCall2, ShouldStartWith, "Bearer eyJ0e")
|
||||
So(authorizationHeaderCall2, ShouldNotEqual, authorizationHeaderCall1)
|
||||
assert.Equal(t, "https://api.nr2.io/some/path", req.URL.String())
|
||||
assert.True(t, strings.HasPrefix(authorizationHeaderCall1, "Bearer eyJ0e"))
|
||||
assert.True(t, strings.HasPrefix(authorizationHeaderCall2, "Bearer eyJ0e"))
|
||||
assert.NotEqual(t, authorizationHeaderCall1, authorizationHeaderCall2)
|
||||
|
||||
Convey("third call to first route should add cached access token", func() {
|
||||
req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
t.Run("third call to first route should add cached access token", func(t *testing.T) {
|
||||
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
client = newFakeHTTPClient([]byte{})
|
||||
proxy3, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
proxy3.route = plugin.Routes[0]
|
||||
ApplyRoute(proxy3.ctx.Req.Context(), req, proxy3.proxyPath, proxy3.route, proxy3.ds)
|
||||
client = newFakeHTTPClient(t, []byte{})
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "pathwithtoken1", &setting.Cfg{})
|
||||
require.NoError(t, err)
|
||||
ApplyRoute(proxy.ctx.Req.Context(), req, proxy.proxyPath, plugin.Routes[0], proxy.ds)
|
||||
|
||||
authorizationHeaderCall3 := req.Header.Get("Authorization")
|
||||
So(req.URL.String(), ShouldEqual, "https://api.nr1.io/some/path")
|
||||
So(authorizationHeaderCall1, ShouldStartWith, "Bearer eyJ0e")
|
||||
So(authorizationHeaderCall3, ShouldStartWith, "Bearer eyJ0e")
|
||||
So(authorizationHeaderCall3, ShouldEqual, authorizationHeaderCall1)
|
||||
assert.Equal(t, "https://api.nr1.io/some/path", req.URL.String())
|
||||
assert.True(t, strings.HasPrefix(authorizationHeaderCall1, "Bearer eyJ0e"))
|
||||
assert.True(t, strings.HasPrefix(authorizationHeaderCall3, "Bearer eyJ0e"))
|
||||
assert.Equal(t, authorizationHeaderCall1, authorizationHeaderCall3)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When proxying graphite", func() {
|
||||
t.Run("When proxying graphite", func(t *testing.T) {
|
||||
origBuildVer := setting.BuildVersion
|
||||
t.Cleanup(func() {
|
||||
setting.BuildVersion = origBuildVer
|
||||
})
|
||||
setting.BuildVersion = "5.3.0"
|
||||
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
ds := &models.DataSource{Url: "htttp://graphite:8080", Type: models.DS_GRAPHITE}
|
||||
ctx := &models.ReqContext{}
|
||||
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "/render", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
|
||||
Convey("Can translate request url and path", func() {
|
||||
So(req.URL.Host, ShouldEqual, "graphite:8080")
|
||||
So(req.URL.Path, ShouldEqual, "/render")
|
||||
So(req.Header.Get("User-Agent"), ShouldEqual, "Grafana/5.3.0")
|
||||
t.Run("Can translate request URL and path", func(t *testing.T) {
|
||||
assert.Equal(t, "graphite:8080", req.URL.Host)
|
||||
assert.Equal(t, "/render", req.URL.Path)
|
||||
assert.Equal(t, "Grafana/5.3.0", req.Header.Get("User-Agent"))
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When proxying InfluxDB", func() {
|
||||
t.Run("When proxying InfluxDB", func(t *testing.T) {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
|
||||
ds := &models.DataSource{
|
||||
@@ -298,22 +317,20 @@ func TestDSRouteRule(t *testing.T) {
|
||||
|
||||
ctx := &models.ReqContext{}
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
|
||||
Convey("Should add db to url", func() {
|
||||
So(req.URL.Path, ShouldEqual, "/db/site/")
|
||||
})
|
||||
assert.Equal(t, "/db/site/", req.URL.Path)
|
||||
})
|
||||
|
||||
Convey("When proxying a data source with no keepCookies specified", func() {
|
||||
t.Run("When proxying a data source with no keepCookies specified", func(t *testing.T) {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
|
||||
json, _ := simplejson.NewJson([]byte(`{"keepCookies": []}`))
|
||||
json, err := simplejson.NewJson([]byte(`{"keepCookies": []}`))
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
Type: models.DS_GRAPHITE,
|
||||
@@ -323,24 +340,24 @@ func TestDSRouteRule(t *testing.T) {
|
||||
|
||||
ctx := &models.ReqContext{}
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
requestURL, _ := url.Parse("http://grafana.com/sub")
|
||||
requestURL, err := url.Parse("http://grafana.com/sub")
|
||||
require.NoError(t, err)
|
||||
req := http.Request{URL: requestURL, Header: make(http.Header)}
|
||||
cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
|
||||
req.Header.Set("Cookie", cookies)
|
||||
|
||||
proxy.getDirector()(&req)
|
||||
|
||||
Convey("Should clear all cookies", func() {
|
||||
So(req.Header.Get("Cookie"), ShouldEqual, "")
|
||||
})
|
||||
assert.Equal(t, "", req.Header.Get("Cookie"))
|
||||
})
|
||||
|
||||
Convey("When proxying a data source with keep cookies specified", func() {
|
||||
t.Run("When proxying a data source with keep cookies specified", func(t *testing.T) {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
|
||||
json, _ := simplejson.NewJson([]byte(`{"keepCookies": ["JSESSION_ID"]}`))
|
||||
json, err := simplejson.NewJson([]byte(`{"keepCookies": ["JSESSION_ID"]}`))
|
||||
require.NoError(t, err)
|
||||
|
||||
ds := &models.DataSource{
|
||||
Type: models.DS_GRAPHITE,
|
||||
@@ -350,21 +367,20 @@ func TestDSRouteRule(t *testing.T) {
|
||||
|
||||
ctx := &models.ReqContext{}
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
requestURL, _ := url.Parse("http://grafana.com/sub")
|
||||
requestURL, err := url.Parse("http://grafana.com/sub")
|
||||
require.NoError(t, err)
|
||||
req := http.Request{URL: requestURL, Header: make(http.Header)}
|
||||
cookies := "grafana_user=admin; grafana_remember=99; grafana_sess=11; JSESSION_ID=test"
|
||||
req.Header.Set("Cookie", cookies)
|
||||
|
||||
proxy.getDirector()(&req)
|
||||
|
||||
Convey("Should keep named cookies", func() {
|
||||
So(req.Header.Get("Cookie"), ShouldEqual, "JSESSION_ID=test")
|
||||
})
|
||||
assert.Equal(t, "JSESSION_ID=test", req.Header.Get("Cookie"))
|
||||
})
|
||||
|
||||
Convey("When proxying a custom datasource", func() {
|
||||
t.Run("When proxying a custom datasource", func(t *testing.T) {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
ds := &models.DataSource{
|
||||
Type: "custom-datasource",
|
||||
@@ -372,32 +388,32 @@ func TestDSRouteRule(t *testing.T) {
|
||||
}
|
||||
ctx := &models.ReqContext{}
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "/path/to/folder/", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
req.Header.Add("Origin", "grafana.com")
|
||||
req.Header.Add("Referer", "grafana.com")
|
||||
req.Header.Add("X-Canary", "stillthere")
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
|
||||
Convey("Should keep user request (including trailing slash)", func() {
|
||||
So(req.URL.String(), ShouldEqual, "http://host/root/path/to/folder/")
|
||||
assert.Equal(t, "http://host/root/path/to/folder/", req.URL.String())
|
||||
|
||||
assert.Empty(t, req.Header.Get("Origin"))
|
||||
assert.Empty(t, req.Header.Get("Referer"))
|
||||
assert.Equal(t, "stillthere", req.Header.Get("X-Canary"))
|
||||
})
|
||||
|
||||
Convey("Origin and Referer headers should be dropped", func() {
|
||||
So(req.Header.Get("Origin"), ShouldEqual, "")
|
||||
So(req.Header.Get("Referer"), ShouldEqual, "")
|
||||
So(req.Header.Get("X-Canary"), ShouldEqual, "stillthere")
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When proxying a datasource that has oauth token pass-through enabled", func() {
|
||||
t.Run("When proxying a datasource that has oauth token pass-through enabled", func(t *testing.T) {
|
||||
social.SocialMap["generic_oauth"] = &social.SocialGenericOAuth{
|
||||
SocialBase: &social.SocialBase{
|
||||
Config: &oauth2.Config{},
|
||||
},
|
||||
}
|
||||
origAuthSvc := setting.OAuthService
|
||||
t.Cleanup(func() {
|
||||
setting.OAuthService = origAuthSvc
|
||||
})
|
||||
setting.OAuthService = &setting.OAuther{}
|
||||
setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
|
||||
setting.OAuthService.OAuthInfos["generic_oauth"] = &setting.OAuthInfo{}
|
||||
@@ -424,7 +440,8 @@ func TestDSRouteRule(t *testing.T) {
|
||||
}),
|
||||
}
|
||||
|
||||
req, _ := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
req, err := http.NewRequest("GET", "http://localhost/asd", nil)
|
||||
require.NoError(t, err)
|
||||
ctx := &models.ReqContext{
|
||||
SignedInUser: &models.SignedInUser{UserId: 1},
|
||||
Context: &macaron.Context{
|
||||
@@ -432,19 +449,18 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
}
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "/path/to/folder/", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
req, err = http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
|
||||
Convey("Should have access token in header", func() {
|
||||
So(req.Header.Get("Authorization"), ShouldEqual, fmt.Sprintf("%s %s", "Bearer", "testtoken"))
|
||||
})
|
||||
assert.Equal(t, "Bearer testtoken", req.Header.Get("Authorization"))
|
||||
})
|
||||
|
||||
Convey("When SendUserHeader config is enabled", func() {
|
||||
t.Run("When SendUserHeader config is enabled", func(t *testing.T) {
|
||||
req := getDatasourceProxiedRequest(
|
||||
t,
|
||||
&models.ReqContext{
|
||||
SignedInUser: &models.SignedInUser{
|
||||
Login: "test_user",
|
||||
@@ -452,13 +468,12 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
&setting.Cfg{SendUserHeader: true},
|
||||
)
|
||||
Convey("Should add header with username", func() {
|
||||
So(req.Header.Get("X-Grafana-User"), ShouldEqual, "test_user")
|
||||
})
|
||||
assert.Equal(t, "test_user", req.Header.Get("X-Grafana-User"))
|
||||
})
|
||||
|
||||
Convey("When SendUserHeader config is disabled", func() {
|
||||
t.Run("When SendUserHeader config is disabled", func(t *testing.T) {
|
||||
req := getDatasourceProxiedRequest(
|
||||
t,
|
||||
&models.ReqContext{
|
||||
SignedInUser: &models.SignedInUser{
|
||||
Login: "test_user",
|
||||
@@ -466,54 +481,51 @@ func TestDSRouteRule(t *testing.T) {
|
||||
},
|
||||
&setting.Cfg{SendUserHeader: false},
|
||||
)
|
||||
Convey("Should not add header with username", func() {
|
||||
// Get will return empty string even if header is not set
|
||||
So(req.Header.Get("X-Grafana-User"), ShouldEqual, "")
|
||||
})
|
||||
assert.Empty(t, req.Header.Get("X-Grafana-User"))
|
||||
})
|
||||
|
||||
Convey("When SendUserHeader config is enabled but user is anonymous", func() {
|
||||
t.Run("When SendUserHeader config is enabled but user is anonymous", func(t *testing.T) {
|
||||
req := getDatasourceProxiedRequest(
|
||||
t,
|
||||
&models.ReqContext{
|
||||
SignedInUser: &models.SignedInUser{IsAnonymous: true},
|
||||
},
|
||||
&setting.Cfg{SendUserHeader: true},
|
||||
)
|
||||
Convey("Should not add header with username", func() {
|
||||
// Get will return empty string even if header is not set
|
||||
So(req.Header.Get("X-Grafana-User"), ShouldEqual, "")
|
||||
})
|
||||
assert.Empty(t, req.Header.Get("X-Grafana-User"))
|
||||
})
|
||||
|
||||
Convey("When proxying data source proxy should handle authentication", func() {
|
||||
tests := []*Test{
|
||||
createAuthTest(models.DS_INFLUXDB_08, AUTHTYPE_PASSWORD, AUTHCHECK_QUERY, false),
|
||||
createAuthTest(models.DS_INFLUXDB_08, AUTHTYPE_PASSWORD, AUTHCHECK_QUERY, true),
|
||||
createAuthTest(models.DS_INFLUXDB, AUTHTYPE_PASSWORD, AUTHCHECK_HEADER, true),
|
||||
createAuthTest(models.DS_INFLUXDB, AUTHTYPE_PASSWORD, AUTHCHECK_HEADER, false),
|
||||
createAuthTest(models.DS_INFLUXDB, AUTHTYPE_BASIC, AUTHCHECK_HEADER, true),
|
||||
createAuthTest(models.DS_INFLUXDB, AUTHTYPE_BASIC, AUTHCHECK_HEADER, false),
|
||||
t.Run("When proxying data source proxy should handle authentication", func(t *testing.T) {
|
||||
tests := []*testCase{
|
||||
createAuthTest(t, models.DS_INFLUXDB_08, authTypePassword, authCheckQuery, false),
|
||||
createAuthTest(t, models.DS_INFLUXDB_08, authTypePassword, authCheckQuery, true),
|
||||
createAuthTest(t, models.DS_INFLUXDB, authTypePassword, authCheckHeader, true),
|
||||
createAuthTest(t, models.DS_INFLUXDB, authTypePassword, authCheckHeader, false),
|
||||
createAuthTest(t, models.DS_INFLUXDB, authTypeBasic, authCheckHeader, true),
|
||||
createAuthTest(t, models.DS_INFLUXDB, authTypeBasic, authCheckHeader, false),
|
||||
|
||||
// These two should be enough for any other datasource at the moment. Proxy has special handling
|
||||
// only for Influx, others have the same path and only BasicAuth. Non BasicAuth datasources
|
||||
// do not go through proxy but through TSDB API which is not tested here.
|
||||
createAuthTest(models.DS_ES, AUTHTYPE_BASIC, AUTHCHECK_HEADER, false),
|
||||
createAuthTest(models.DS_ES, AUTHTYPE_BASIC, AUTHCHECK_HEADER, true),
|
||||
createAuthTest(t, models.DS_ES, authTypeBasic, authCheckHeader, false),
|
||||
createAuthTest(t, models.DS_ES, authTypeBasic, authCheckHeader, true),
|
||||
}
|
||||
for _, test := range tests {
|
||||
models.ClearDSDecryptionCache()
|
||||
runDatasourceAuthTest(test)
|
||||
runDatasourceAuthTest(t, test)
|
||||
}
|
||||
})
|
||||
|
||||
Convey("HandleRequest()", func() {
|
||||
t.Run("HandleRequest()", func(t *testing.T) {
|
||||
var writeErr error
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.SetCookie(w, &http.Cookie{Name: "flavor", Value: "chocolateChip"})
|
||||
w.WriteHeader(200)
|
||||
_, writeErr = w.Write([]byte("I am the backend"))
|
||||
}))
|
||||
defer backend.Close()
|
||||
t.Cleanup(backend.Close)
|
||||
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
ds := &models.DataSource{Url: backend.URL, Type: models.DS_GRAPHITE}
|
||||
@@ -521,7 +533,7 @@ func TestDSRouteRule(t *testing.T) {
|
||||
responseRecorder := &CloseNotifierResponseRecorder{
|
||||
ResponseRecorder: httptest.NewRecorder(),
|
||||
}
|
||||
defer responseRecorder.Close()
|
||||
t.Cleanup(responseRecorder.Close)
|
||||
|
||||
setupCtx := func(fn func(http.ResponseWriter)) *models.ReqContext {
|
||||
responseWriter := macaron.NewResponseWriter("GET", responseRecorder)
|
||||
@@ -540,32 +552,30 @@ func TestDSRouteRule(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
Convey("When response header Set-Cookie is not set should remove proxied Set-Cookie header", func() {
|
||||
t.Run("When response header Set-Cookie is not set should remove proxied Set-Cookie header", func(t *testing.T) {
|
||||
writeErr = nil
|
||||
ctx := setupCtx(nil)
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "/render", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.HandleRequest()
|
||||
|
||||
So(writeErr, ShouldBeNil)
|
||||
So(proxy.ctx.Resp.Header().Get("Set-Cookie"), ShouldBeEmpty)
|
||||
require.NoError(t, writeErr)
|
||||
assert.Empty(t, proxy.ctx.Resp.Header().Get("Set-Cookie"))
|
||||
})
|
||||
|
||||
Convey("When response header Set-Cookie is set should remove proxied Set-Cookie header and restore the original Set-Cookie header", func() {
|
||||
t.Run("When response header Set-Cookie is set should remove proxied Set-Cookie header and restore the original Set-Cookie header", func(t *testing.T) {
|
||||
writeErr = nil
|
||||
ctx := setupCtx(func(w http.ResponseWriter) {
|
||||
w.Header().Set("Set-Cookie", "important_cookie=important_value")
|
||||
})
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "/render", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.HandleRequest()
|
||||
|
||||
So(writeErr, ShouldBeNil)
|
||||
So(proxy.ctx.Resp.Header().Get("Set-Cookie"), ShouldEqual,
|
||||
"important_cookie=important_value")
|
||||
})
|
||||
require.NoError(t, writeErr)
|
||||
assert.Equal(t, "important_cookie=important_value", proxy.ctx.Resp.Header().Get("Set-Cookie"))
|
||||
})
|
||||
})
|
||||
}
|
||||
@@ -672,7 +682,7 @@ func (r *CloseNotifierResponseRecorder) Close() {
|
||||
}
|
||||
|
||||
// getDatasourceProxiedRequest is a helper for easier setup of tests based on global config and ReqContext.
|
||||
func getDatasourceProxiedRequest(ctx *models.ReqContext, cfg *setting.Cfg) *http.Request {
|
||||
func getDatasourceProxiedRequest(t *testing.T, ctx *models.ReqContext, cfg *setting.Cfg) *http.Request {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
|
||||
ds := &models.DataSource{
|
||||
@@ -681,25 +691,28 @@ func getDatasourceProxiedRequest(ctx *models.ReqContext, cfg *setting.Cfg) *http
|
||||
}
|
||||
|
||||
proxy, err := NewDataSourceProxy(ds, plugin, ctx, "", cfg)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
return req
|
||||
}
|
||||
|
||||
type httpClientStub struct {
|
||||
t *testing.T
|
||||
fakeBody []byte
|
||||
}
|
||||
|
||||
func (c *httpClientStub) Do(req *http.Request) (*http.Response, error) {
|
||||
bodyJSON, _ := simplejson.NewJson(c.fakeBody)
|
||||
bodyJSON, err := simplejson.NewJson(c.fakeBody)
|
||||
require.NoError(c.t, err)
|
||||
_, passedTokenCacheTest := bodyJSON.CheckGet("expires_on")
|
||||
So(passedTokenCacheTest, ShouldBeTrue)
|
||||
require.True(c.t, passedTokenCacheTest)
|
||||
|
||||
bodyJSON.Set("expires_on", fmt.Sprint(time.Now().Add(time.Second*60).Unix()))
|
||||
body, _ := bodyJSON.MarshalJSON()
|
||||
body, err := bodyJSON.MarshalJSON()
|
||||
require.NoError(c.t, err)
|
||||
resp := &http.Response{
|
||||
Body: ioutil.NopCloser(bytes.NewReader(body)),
|
||||
}
|
||||
@@ -707,39 +720,40 @@ func (c *httpClientStub) Do(req *http.Request) (*http.Response, error) {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func newFakeHTTPClient(fakeBody []byte) httpClient {
|
||||
func newFakeHTTPClient(t *testing.T, fakeBody []byte) httpClient {
|
||||
return &httpClientStub{
|
||||
t: t,
|
||||
fakeBody: fakeBody,
|
||||
}
|
||||
}
|
||||
|
||||
type Test struct {
|
||||
type testCase struct {
|
||||
datasource *models.DataSource
|
||||
checkReq func(req *http.Request)
|
||||
}
|
||||
|
||||
const (
|
||||
AUTHTYPE_PASSWORD = "password"
|
||||
AUTHTYPE_BASIC = "basic"
|
||||
authTypePassword = "password"
|
||||
authTypeBasic = "basic"
|
||||
)
|
||||
|
||||
const (
|
||||
AUTHCHECK_QUERY = "query"
|
||||
AUTHCHECK_HEADER = "header"
|
||||
authCheckQuery = "query"
|
||||
authCheckHeader = "header"
|
||||
)
|
||||
|
||||
func createAuthTest(dsType string, authType string, authCheck string, useSecureJsonData bool) *Test {
|
||||
func createAuthTest(t *testing.T, dsType string, authType string, authCheck string, useSecureJsonData bool) *testCase {
|
||||
// Basic user:password
|
||||
base64AthHeader := "Basic dXNlcjpwYXNzd29yZA=="
|
||||
base64AuthHeader := "Basic dXNlcjpwYXNzd29yZA=="
|
||||
|
||||
test := &Test{
|
||||
test := &testCase{
|
||||
datasource: &models.DataSource{
|
||||
Type: dsType,
|
||||
JsonData: simplejson.New(),
|
||||
},
|
||||
}
|
||||
var message string
|
||||
if authType == AUTHTYPE_PASSWORD {
|
||||
if authType == authTypePassword {
|
||||
message = fmt.Sprintf("%v should add username and password", dsType)
|
||||
test.datasource.User = "user"
|
||||
if useSecureJsonData {
|
||||
@@ -766,35 +780,31 @@ func createAuthTest(dsType string, authType string, authCheck string, useSecureJ
|
||||
message += " from securejsondata"
|
||||
}
|
||||
|
||||
if authCheck == AUTHCHECK_QUERY {
|
||||
if authCheck == authCheckQuery {
|
||||
message += " to query params"
|
||||
test.checkReq = func(req *http.Request) {
|
||||
Convey(message, func() {
|
||||
queryVals := req.URL.Query()
|
||||
So(queryVals["u"][0], ShouldEqual, "user")
|
||||
So(queryVals["p"][0], ShouldEqual, "password")
|
||||
})
|
||||
assert.Equal(t, "user", queryVals["u"][0], message)
|
||||
assert.Equal(t, "password", queryVals["p"][0], message)
|
||||
}
|
||||
} else {
|
||||
message += " to auth header"
|
||||
test.checkReq = func(req *http.Request) {
|
||||
Convey(message, func() {
|
||||
So(req.Header.Get("Authorization"), ShouldEqual, base64AthHeader)
|
||||
})
|
||||
assert.Equal(t, base64AuthHeader, req.Header.Get("Authorization"), message)
|
||||
}
|
||||
}
|
||||
|
||||
return test
|
||||
}
|
||||
|
||||
func runDatasourceAuthTest(test *Test) {
|
||||
func runDatasourceAuthTest(t *testing.T, test *testCase) {
|
||||
plugin := &plugins.DataSourcePlugin{}
|
||||
ctx := &models.ReqContext{}
|
||||
proxy, err := NewDataSourceProxy(test.datasource, plugin, ctx, "", &setting.Cfg{})
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, "http://grafana.com/sub", nil)
|
||||
So(err, ShouldBeNil)
|
||||
require.NoError(t, err)
|
||||
|
||||
proxy.getDirector()(req)
|
||||
|
||||
|
@@ -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,8 +42,7 @@ 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) {
|
||||
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 {
|
||||
@@ -58,19 +57,17 @@ func TestTeamApiEndpoint(t *testing.T) {
|
||||
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)
|
||||
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) {
|
||||
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 {
|
||||
@@ -85,13 +82,12 @@ func TestTeamApiEndpoint(t *testing.T) {
|
||||
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,12 +10,11 @@ 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() {
|
||||
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
||||
mockResult := models.SearchUserQueryResult{
|
||||
Users: []*models.UserSearchHitDTO{
|
||||
{Name: "user1"},
|
||||
@@ -24,7 +23,7 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
TotalCount: 2,
|
||||
}
|
||||
|
||||
loggedInUserScenario("When calling GET on", "api/users/:id", func(sc *scenarioContext) {
|
||||
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{
|
||||
@@ -77,7 +76,7 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
require.JSONEq(t, expected, sc.resp.Body.String())
|
||||
})
|
||||
|
||||
loggedInUserScenario("When calling GET on", "/api/users/lookup", func(sc *scenarioContext) {
|
||||
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)
|
||||
@@ -123,7 +122,7 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
require.JSONEq(t, expected, sc.resp.Body.String())
|
||||
})
|
||||
|
||||
loggedInUserScenario("When calling GET on", "/api/users", func(sc *scenarioContext) {
|
||||
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 {
|
||||
@@ -138,15 +137,15 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = SearchUsers
|
||||
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)
|
||||
So(len(respJSON.MustArray()), ShouldEqual, 2)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, 2, len(respJSON.MustArray()))
|
||||
})
|
||||
|
||||
loggedInUserScenario("When calling GET with page and limit querystring parameters on", "/api/users", func(sc *scenarioContext) {
|
||||
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 {
|
||||
@@ -161,11 +160,11 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = SearchUsers
|
||||
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)
|
||||
})
|
||||
|
||||
loggedInUserScenario("When calling GET on", "/api/users/search", func(sc *scenarioContext) {
|
||||
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 {
|
||||
@@ -180,17 +179,17 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
sc.handlerFunc = SearchUsersWithPaging
|
||||
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)
|
||||
require.NoError(t, err)
|
||||
|
||||
So(respJSON.Get("totalCount").MustInt(), ShouldEqual, 2)
|
||||
So(len(respJSON.Get("users").MustArray()), ShouldEqual, 2)
|
||||
assert.Equal(t, 2, respJSON.Get("totalCount").MustInt())
|
||||
assert.Equal(t, 2, len(respJSON.Get("users").MustArray()))
|
||||
})
|
||||
|
||||
loggedInUserScenario("When calling GET with page and perpage querystring parameters on", "/api/users/search", func(sc *scenarioContext) {
|
||||
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 {
|
||||
@@ -205,8 +204,7 @@ func TestUserApiEndpoint(t *testing.T) {
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
Convey("When current user gets auth tokens for a non-existing user", t, func() {
|
||||
userId := int64(0)
|
||||
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
|
||||
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)
|
||||
})
|
||||
})
|
||||
|
||||
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() {
|
||||
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: 200}
|
||||
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() {
|
||||
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() {
|
||||
t.Run("When gets auth tokens for a user", func(t *testing.T) {
|
||||
currentToken := &models.UserToken{Id: 1}
|
||||
|
||||
getUserAuthTokensInternalScenario(t, "Should be successful", currentToken, func(sc *scenarioContext) {
|
||||
bus.AddHandler("test", func(cmd *models.GetUserByIdQuery) error {
|
||||
cmd.Result = &models.User{Id: TestUserID}
|
||||
cmd.Result = &models.User{Id: testUserID}
|
||||
return nil
|
||||
})
|
||||
|
||||
currentToken := &models.UserToken{Id: 1}
|
||||
|
||||
getUserAuthTokensInternalScenario("Should be successful", currentToken, func(sc *scenarioContext) {
|
||||
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))
|
||||
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())
|
||||
|
||||
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, "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))
|
||||
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())
|
||||
|
||||
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, "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
|
||||
|
Reference in New Issue
Block a user