mirror of
https://github.com/grafana/grafana.git
synced 2024-12-29 10:21:41 -06:00
325f7a789e
* Delete duplicate models for user * Use new models in some tests * Add auth model conversion back
326 lines
11 KiB
Go
326 lines
11 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/oauth2"
|
|
|
|
"github.com/grafana/grafana/pkg/api/dtos"
|
|
"github.com/grafana/grafana/pkg/api/response"
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
|
"github.com/grafana/grafana/pkg/components/simplejson"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/usagestats"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
|
|
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
|
|
authinfostore "github.com/grafana/grafana/pkg/services/login/authinfoservice/database"
|
|
"github.com/grafana/grafana/pkg/services/login/logintest"
|
|
"github.com/grafana/grafana/pkg/services/org/orgimpl"
|
|
"github.com/grafana/grafana/pkg/services/quota/quotatest"
|
|
"github.com/grafana/grafana/pkg/services/searchusers"
|
|
"github.com/grafana/grafana/pkg/services/searchusers/filters"
|
|
"github.com/grafana/grafana/pkg/services/secrets/database"
|
|
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
|
|
"github.com/grafana/grafana/pkg/services/sqlstore/mockstore"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/userimpl"
|
|
"github.com/grafana/grafana/pkg/services/user/usertest"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
func TestUserAPIEndpoint_userLoggedIn(t *testing.T) {
|
|
settings := setting.NewCfg()
|
|
sqlStore := db.InitTestDB(t)
|
|
hs := &HTTPServer{
|
|
Cfg: settings,
|
|
SQLStore: sqlStore,
|
|
AccessControl: acmock.New(),
|
|
}
|
|
|
|
mockResult := user.SearchUserQueryResult{
|
|
Users: []*user.UserSearchHitDTO{
|
|
{Name: "user1"},
|
|
{Name: "user2"},
|
|
},
|
|
TotalCount: 2,
|
|
}
|
|
mock := mockstore.NewSQLStoreMock()
|
|
userMock := usertest.NewUserServiceFake()
|
|
loggedInUserScenario(t, "When calling GET on", "api/users/1", "api/users/:id", func(sc *scenarioContext) {
|
|
fakeNow := time.Date(2019, 2, 11, 17, 30, 40, 0, time.UTC)
|
|
secretsService := secretsManager.SetupTestService(t, database.ProvideSecretsStore(sqlStore))
|
|
authInfoStore := authinfostore.ProvideAuthInfoStore(sqlStore, secretsService, userMock)
|
|
srv := authinfoservice.ProvideAuthInfoService(
|
|
&authinfoservice.OSSUserProtectionImpl{},
|
|
authInfoStore,
|
|
&usagestats.UsageStatsMock{},
|
|
)
|
|
hs.authInfoService = srv
|
|
orgSvc, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotatest.New(false, nil))
|
|
require.NoError(t, err)
|
|
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, sc.cfg, nil, nil, quotatest.New(false, nil))
|
|
require.NoError(t, err)
|
|
hs.userService = userSvc
|
|
|
|
createUserCmd := user.CreateUserCommand{
|
|
Email: fmt.Sprint("user", "@test.com"),
|
|
Name: "user",
|
|
Login: "loginuser",
|
|
IsAdmin: true,
|
|
}
|
|
usr, err := userSvc.CreateUserForTests(context.Background(), &createUserCmd)
|
|
require.NoError(t, err)
|
|
|
|
sc.handlerFunc = hs.GetUserByID
|
|
|
|
token := &oauth2.Token{
|
|
AccessToken: "testaccess",
|
|
RefreshToken: "testrefresh",
|
|
Expiry: time.Now(),
|
|
TokenType: "Bearer",
|
|
}
|
|
idToken := "testidtoken"
|
|
token = token.WithExtra(map[string]interface{}{"id_token": idToken})
|
|
login := "loginuser"
|
|
query := &models.GetUserByAuthInfoQuery{AuthModule: "test", AuthId: "test", UserLookupParams: models.UserLookupParams{Login: &login}}
|
|
cmd := &models.UpdateAuthInfoCommand{
|
|
UserId: usr.ID,
|
|
AuthId: query.AuthId,
|
|
AuthModule: query.AuthModule,
|
|
OAuthToken: token,
|
|
}
|
|
err = srv.UpdateAuthInfo(context.Background(), cmd)
|
|
require.NoError(t, err)
|
|
avatarUrl := dtos.GetGravatarUrl("@test.com")
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{"id": fmt.Sprintf("%v", usr.ID)}).exec()
|
|
|
|
expected := user.UserProfileDTO{
|
|
ID: 1,
|
|
Email: "user@test.com",
|
|
Name: "user",
|
|
Login: "loginuser",
|
|
OrgID: 1,
|
|
IsGrafanaAdmin: true,
|
|
AuthLabels: []string{},
|
|
CreatedAt: fakeNow,
|
|
UpdatedAt: fakeNow,
|
|
AvatarUrl: avatarUrl,
|
|
}
|
|
|
|
var resp user.UserProfileDTO
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
err = json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
resp.CreatedAt = fakeNow
|
|
resp.UpdatedAt = fakeNow
|
|
resp.AvatarUrl = avatarUrl
|
|
require.EqualValues(t, expected, resp)
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET on", "/api/users/lookup", "/api/users/lookup", func(sc *scenarioContext) {
|
|
createUserCmd := user.CreateUserCommand{
|
|
Email: fmt.Sprint("admin", "@test.com"),
|
|
Name: "admin",
|
|
Login: "admin",
|
|
IsAdmin: true,
|
|
}
|
|
orgSvc, err := orgimpl.ProvideService(sqlStore, sqlStore.Cfg, quotatest.New(false, nil))
|
|
require.NoError(t, err)
|
|
userSvc, err := userimpl.ProvideService(sqlStore, orgSvc, sc.cfg, nil, nil, quotatest.New(false, nil))
|
|
require.NoError(t, err)
|
|
_, err = userSvc.Create(context.Background(), &createUserCmd)
|
|
require.Nil(t, err)
|
|
|
|
sc.handlerFunc = hs.GetUserByLoginOrEmail
|
|
|
|
userMock := usertest.NewUserServiceFake()
|
|
userMock.ExpectedUser = &user.User{ID: 2}
|
|
sc.userService = userMock
|
|
hs.userService = userMock
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{"loginOrEmail": "admin@test.com"}).exec()
|
|
|
|
var resp user.UserProfileDTO
|
|
require.Equal(t, http.StatusOK, sc.resp.Code)
|
|
err = json.Unmarshal(sc.resp.Body.Bytes(), &resp)
|
|
require.NoError(t, err)
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET on", "/api/users", "/api/users", func(sc *scenarioContext) {
|
|
userMock.ExpectedSearchUsers = mockResult
|
|
|
|
searchUsersService := searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), userMock)
|
|
sc.handlerFunc = searchUsersService.SearchUsers
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, len(respJSON.MustArray()))
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET with page and limit querystring parameters on", "/api/users", "/api/users", func(sc *scenarioContext) {
|
|
userMock.ExpectedSearchUsers = mockResult
|
|
|
|
searchUsersService := searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), userMock)
|
|
sc.handlerFunc = searchUsersService.SearchUsers
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
|
|
|
|
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, len(respJSON.MustArray()))
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET on", "/api/users/search", "/api/users/search", func(sc *scenarioContext) {
|
|
userMock.ExpectedSearchUsers = mockResult
|
|
|
|
searchUsersService := searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), userMock)
|
|
sc.handlerFunc = searchUsersService.SearchUsersWithPaging
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{}).exec()
|
|
|
|
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 1, respJSON.Get("page").MustInt())
|
|
assert.Equal(t, 1000, respJSON.Get("perPage").MustInt())
|
|
assert.Equal(t, 2, respJSON.Get("totalCount").MustInt())
|
|
assert.Equal(t, 2, len(respJSON.Get("users").MustArray()))
|
|
}, mock)
|
|
|
|
loggedInUserScenario(t, "When calling GET with page and perpage querystring parameters on", "/api/users/search", "/api/users/search", func(sc *scenarioContext) {
|
|
userMock.ExpectedSearchUsers = mockResult
|
|
|
|
searchUsersService := searchusers.ProvideUsersService(filters.ProvideOSSSearchUserFilter(), userMock)
|
|
sc.handlerFunc = searchUsersService.SearchUsersWithPaging
|
|
sc.fakeReqWithParams("GET", sc.url, map[string]string{"perpage": "10", "page": "2"}).exec()
|
|
|
|
respJSON, err := simplejson.NewJson(sc.resp.Body.Bytes())
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, 2, respJSON.Get("page").MustInt())
|
|
assert.Equal(t, 10, respJSON.Get("perPage").MustInt())
|
|
}, mock)
|
|
}
|
|
|
|
func TestHTTPServer_UpdateUser(t *testing.T) {
|
|
settings := setting.NewCfg()
|
|
sqlStore := db.InitTestDB(t)
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: settings,
|
|
SQLStore: sqlStore,
|
|
AccessControl: acmock.New(),
|
|
}
|
|
|
|
updateUserCommand := user.UpdateUserCommand{
|
|
Email: fmt.Sprint("admin", "@test.com"),
|
|
Name: "admin",
|
|
Login: "admin",
|
|
UserID: 1,
|
|
}
|
|
|
|
updateUserScenario(t, updateUserContext{
|
|
desc: "Should return 403 when the current User is an external user",
|
|
url: "/api/users/1",
|
|
routePattern: "/api/users/:id",
|
|
cmd: updateUserCommand,
|
|
fn: func(sc *scenarioContext) {
|
|
sc.authInfoService.ExpectedUserAuth = &models.UserAuth{}
|
|
sc.fakeReqWithParams("PUT", sc.url, map[string]string{"id": "1"}).exec()
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
},
|
|
}, hs)
|
|
}
|
|
|
|
type updateUserContext struct {
|
|
desc string
|
|
url string
|
|
routePattern string
|
|
cmd user.UpdateUserCommand
|
|
fn scenarioFunc
|
|
}
|
|
|
|
func updateUserScenario(t *testing.T, ctx updateUserContext, hs *HTTPServer) {
|
|
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
|
sc := setupScenarioContext(t, ctx.url)
|
|
|
|
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
|
hs.authInfoService = sc.authInfoService
|
|
|
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
|
c.Req.Body = mockRequestBody(ctx.cmd)
|
|
c.Req.Header.Add("Content-Type", "application/json")
|
|
sc.context = c
|
|
sc.context.OrgID = testOrgID
|
|
sc.context.UserID = testUserID
|
|
|
|
return hs.UpdateUser(c)
|
|
})
|
|
|
|
sc.m.Put(ctx.routePattern, sc.defaultHandler)
|
|
|
|
ctx.fn(sc)
|
|
})
|
|
}
|
|
|
|
func TestHTTPServer_UpdateSignedInUser(t *testing.T) {
|
|
settings := setting.NewCfg()
|
|
sqlStore := db.InitTestDB(t)
|
|
|
|
hs := &HTTPServer{
|
|
Cfg: settings,
|
|
SQLStore: sqlStore,
|
|
AccessControl: acmock.New(),
|
|
}
|
|
|
|
updateUserCommand := user.UpdateUserCommand{
|
|
Email: fmt.Sprint("admin", "@test.com"),
|
|
Name: "admin",
|
|
Login: "admin",
|
|
UserID: 1,
|
|
}
|
|
|
|
updateSignedInUserScenario(t, updateUserContext{
|
|
desc: "Should return 403 when the current User is an external user",
|
|
url: "/api/users/",
|
|
routePattern: "/api/users/",
|
|
cmd: updateUserCommand,
|
|
fn: func(sc *scenarioContext) {
|
|
sc.authInfoService.ExpectedUserAuth = &models.UserAuth{}
|
|
sc.fakeReqWithParams("PUT", sc.url, map[string]string{"id": "1"}).exec()
|
|
assert.Equal(t, 403, sc.resp.Code)
|
|
},
|
|
}, hs)
|
|
}
|
|
|
|
func updateSignedInUserScenario(t *testing.T, ctx updateUserContext, hs *HTTPServer) {
|
|
t.Run(fmt.Sprintf("%s %s", ctx.desc, ctx.url), func(t *testing.T) {
|
|
sc := setupScenarioContext(t, ctx.url)
|
|
|
|
sc.authInfoService = &logintest.AuthInfoServiceFake{}
|
|
hs.authInfoService = sc.authInfoService
|
|
|
|
sc.defaultHandler = routing.Wrap(func(c *models.ReqContext) response.Response {
|
|
c.Req.Body = mockRequestBody(ctx.cmd)
|
|
c.Req.Header.Add("Content-Type", "application/json")
|
|
sc.context = c
|
|
sc.context.OrgID = testOrgID
|
|
sc.context.UserID = testUserID
|
|
|
|
return hs.UpdateSignedInUser(c)
|
|
})
|
|
|
|
sc.m.Put(ctx.routePattern, sc.defaultHandler)
|
|
|
|
ctx.fn(sc)
|
|
})
|
|
}
|