2017-02-08 07:20:07 -06:00
package api
import (
2021-12-01 08:43:31 -06:00
"context"
2022-01-26 13:24:05 -06:00
"encoding/json"
2020-01-13 10:10:19 -06:00
"fmt"
2019-09-11 07:43:05 -05:00
"net/http"
2017-02-08 07:20:07 -06:00
"testing"
2019-09-11 07:43:05 -05:00
"time"
2017-02-08 07:20:07 -06:00
2022-02-18 04:27:00 -06:00
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
2022-01-26 13:24:05 -06:00
"github.com/grafana/grafana/pkg/api/dtos"
2022-11-14 06:11:26 -06:00
"github.com/grafana/grafana/pkg/api/response"
"github.com/grafana/grafana/pkg/api/routing"
2022-02-18 04:27:00 -06:00
"github.com/grafana/grafana/pkg/components/simplejson"
2022-10-19 08:02:15 -05:00
"github.com/grafana/grafana/pkg/infra/db"
2023-01-18 09:01:25 -06:00
"github.com/grafana/grafana/pkg/infra/db/dbtest"
2022-06-22 07:16:28 -05:00
"github.com/grafana/grafana/pkg/infra/usagestats"
2022-02-18 04:27:00 -06:00
"github.com/grafana/grafana/pkg/models"
acmock "github.com/grafana/grafana/pkg/services/accesscontrol/mock"
2023-01-27 01:50:36 -06:00
contextmodel "github.com/grafana/grafana/pkg/services/contexthandler/model"
2022-01-26 13:24:05 -06:00
"github.com/grafana/grafana/pkg/services/login/authinfoservice"
2022-02-03 11:23:45 -06:00
authinfostore "github.com/grafana/grafana/pkg/services/login/authinfoservice/database"
2022-11-14 06:11:26 -06:00
"github.com/grafana/grafana/pkg/services/login/logintest"
2022-12-07 10:03:22 -06:00
"github.com/grafana/grafana/pkg/services/org/orgimpl"
2022-11-14 13:08:10 -06:00
"github.com/grafana/grafana/pkg/services/quota/quotatest"
2022-02-18 04:27:00 -06:00
"github.com/grafana/grafana/pkg/services/searchusers"
2021-10-07 09:06:16 -05:00
"github.com/grafana/grafana/pkg/services/searchusers/filters"
2022-01-26 13:24:05 -06:00
"github.com/grafana/grafana/pkg/services/secrets/database"
secretsManager "github.com/grafana/grafana/pkg/services/secrets/manager"
2022-06-28 07:32:25 -05:00
"github.com/grafana/grafana/pkg/services/user"
2022-09-27 06:58:49 -05:00
"github.com/grafana/grafana/pkg/services/user/userimpl"
2022-08-02 09:58:05 -05:00
"github.com/grafana/grafana/pkg/services/user/usertest"
2022-01-05 02:59:17 -06:00
"github.com/grafana/grafana/pkg/setting"
2017-02-08 07:20:07 -06:00
)
2020-11-13 02:52:38 -06:00
func TestUserAPIEndpoint_userLoggedIn ( t * testing . T ) {
2022-01-05 02:59:17 -06:00
settings := setting . NewCfg ( )
2022-10-19 08:02:15 -05:00
sqlStore := db . InitTestDB ( t )
2022-01-26 13:24:05 -06:00
hs := & HTTPServer {
2022-02-18 04:27:00 -06:00
Cfg : settings ,
SQLStore : sqlStore ,
2022-03-21 12:28:39 -05:00
AccessControl : acmock . New ( ) ,
2022-01-26 13:24:05 -06:00
}
2022-01-05 02:59:17 -06:00
2022-08-16 07:24:57 -05:00
mockResult := user . SearchUserQueryResult {
Users : [ ] * user . UserSearchHitDTO {
2020-11-13 02:52:38 -06:00
{ Name : "user1" } ,
{ Name : "user2" } ,
} ,
TotalCount : 2 ,
}
2023-01-18 09:01:25 -06:00
mock := dbtest . NewFakeDB ( )
2022-08-16 07:24:57 -05:00
userMock := usertest . NewUserServiceFake ( )
2022-01-14 10:55:57 -06:00
loggedInUserScenario ( t , "When calling GET on" , "api/users/1" , "api/users/:id" , func ( sc * scenarioContext ) {
2020-11-13 02:52:38 -06:00
fakeNow := time . Date ( 2019 , 2 , 11 , 17 , 30 , 40 , 0 , time . UTC )
2022-01-26 13:24:05 -06:00
secretsService := secretsManager . SetupTestService ( t , database . ProvideSecretsStore ( sqlStore ) )
2022-08-16 07:24:57 -05:00
authInfoStore := authinfostore . ProvideAuthInfoStore ( sqlStore , secretsService , userMock )
2022-06-22 07:16:28 -05:00
srv := authinfoservice . ProvideAuthInfoService (
& authinfoservice . OSSUserProtectionImpl { } ,
authInfoStore ,
& usagestats . UsageStatsMock { } ,
)
2022-01-26 13:24:05 -06:00
hs . authInfoService = srv
2022-12-07 10:03:22 -06:00
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
2022-01-26 13:24:05 -06:00
2022-06-28 07:32:25 -05:00
createUserCmd := user . CreateUserCommand {
2022-01-26 13:24:05 -06:00
Email : fmt . Sprint ( "user" , "@test.com" ) ,
Name : "user" ,
Login : "loginuser" ,
IsAdmin : true ,
}
2023-01-03 08:25:35 -06:00
usr , err := userSvc . CreateUserForTests ( context . Background ( ) , & createUserCmd )
2022-11-14 13:08:10 -06:00
require . NoError ( t , err )
2020-11-13 02:52:38 -06:00
2022-01-05 02:59:17 -06:00
sc . handlerFunc = hs . GetUserByID
2019-09-11 07:43:05 -05:00
2022-01-26 13:24:05 -06:00
token := & oauth2 . Token {
AccessToken : "testaccess" ,
RefreshToken : "testrefresh" ,
Expiry : time . Now ( ) ,
TokenType : "Bearer" ,
}
idToken := "testidtoken"
token = token . WithExtra ( map [ string ] interface { } { "id_token" : idToken } )
2022-07-15 04:21:09 -05:00
login := "loginuser"
query := & models . GetUserByAuthInfoQuery { AuthModule : "test" , AuthId : "test" , UserLookupParams : models . UserLookupParams { Login : & login } }
2022-01-26 13:24:05 -06:00
cmd := & models . UpdateAuthInfoCommand {
2023-01-03 08:25:35 -06:00
UserId : usr . ID ,
2022-01-26 13:24:05 -06:00
AuthId : query . AuthId ,
AuthModule : query . AuthModule ,
OAuthToken : token ,
}
err = srv . UpdateAuthInfo ( context . Background ( ) , cmd )
require . NoError ( t , err )
avatarUrl := dtos . GetGravatarUrl ( "@test.com" )
2023-01-03 08:25:35 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { "id" : fmt . Sprintf ( "%v" , usr . ID ) } ) . exec ( )
2022-01-26 13:24:05 -06:00
2023-01-03 08:25:35 -06:00
expected := user . UserProfileDTO {
ID : 1 ,
2022-01-26 13:24:05 -06:00
Email : "user@test.com" ,
Name : "user" ,
Login : "loginuser" ,
2023-01-03 08:25:35 -06:00
OrgID : 1 ,
2022-01-26 13:24:05 -06:00
IsGrafanaAdmin : true ,
AuthLabels : [ ] string { } ,
CreatedAt : fakeNow ,
UpdatedAt : fakeNow ,
AvatarUrl : avatarUrl ,
}
2023-01-03 08:25:35 -06:00
var resp user . UserProfileDTO
2020-11-13 02:52:38 -06:00
require . Equal ( t , http . StatusOK , sc . resp . Code )
2022-01-26 13:24:05 -06:00
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 )
2022-02-03 02:20:20 -06:00
} , mock )
2020-11-13 02:52:38 -06:00
2022-01-14 10:55:57 -06:00
loggedInUserScenario ( t , "When calling GET on" , "/api/users/lookup" , "/api/users/lookup" , func ( sc * scenarioContext ) {
2022-06-28 07:32:25 -05:00
createUserCmd := user . CreateUserCommand {
2022-01-26 13:24:05 -06:00
Email : fmt . Sprint ( "admin" , "@test.com" ) ,
Name : "admin" ,
Login : "admin" ,
IsAdmin : true ,
}
2022-12-07 10:03:22 -06:00
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 )
2022-01-26 13:24:05 -06:00
require . Nil ( t , err )
sc . handlerFunc = hs . GetUserByLoginOrEmail
2022-08-04 06:22:43 -05:00
userMock := usertest . NewUserServiceFake ( )
userMock . ExpectedUser = & user . User { ID : 2 }
sc . userService = userMock
hs . userService = userMock
2022-01-26 13:24:05 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { "loginOrEmail" : "admin@test.com" } ) . exec ( )
2023-01-03 08:25:35 -06:00
var resp user . UserProfileDTO
2020-11-13 02:52:38 -06:00
require . Equal ( t , http . StatusOK , sc . resp . Code )
2022-01-26 13:24:05 -06:00
err = json . Unmarshal ( sc . resp . Body . Bytes ( ) , & resp )
require . NoError ( t , err )
2022-02-03 02:20:20 -06:00
} , mock )
2020-11-13 02:52:38 -06:00
2022-01-14 10:55:57 -06:00
loggedInUserScenario ( t , "When calling GET on" , "/api/users" , "/api/users" , func ( sc * scenarioContext ) {
2022-08-16 07:24:57 -05:00
userMock . ExpectedSearchUsers = mockResult
2020-11-13 02:52:38 -06:00
2022-08-16 07:24:57 -05:00
searchUsersService := searchusers . ProvideUsersService ( filters . ProvideOSSSearchUserFilter ( ) , userMock )
2021-09-29 05:51:49 -05:00
sc . handlerFunc = searchUsersService . SearchUsers
2020-11-13 02:52:38 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
2017-02-08 07:20:07 -06:00
2020-11-13 02:52:38 -06:00
respJSON , err := simplejson . NewJson ( sc . resp . Body . Bytes ( ) )
require . NoError ( t , err )
2022-03-02 04:05:31 -06:00
2020-11-13 02:52:38 -06:00
assert . Equal ( t , 2 , len ( respJSON . MustArray ( ) ) )
2022-02-03 02:20:20 -06:00
} , mock )
2017-02-08 07:20:07 -06:00
2022-01-14 10:55:57 -06:00
loggedInUserScenario ( t , "When calling GET with page and limit querystring parameters on" , "/api/users" , "/api/users" , func ( sc * scenarioContext ) {
2022-08-16 07:24:57 -05:00
userMock . ExpectedSearchUsers = mockResult
2017-02-08 07:20:07 -06:00
2022-08-16 07:24:57 -05:00
searchUsersService := searchusers . ProvideUsersService ( filters . ProvideOSSSearchUserFilter ( ) , userMock )
2021-09-29 05:51:49 -05:00
sc . handlerFunc = searchUsersService . SearchUsers
2020-11-13 02:52:38 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { "perpage" : "10" , "page" : "2" } ) . exec ( )
2017-02-08 07:20:07 -06:00
2022-03-02 04:05:31 -06:00
respJSON , err := simplejson . NewJson ( sc . resp . Body . Bytes ( ) )
require . NoError ( t , err )
assert . Equal ( t , 2 , len ( respJSON . MustArray ( ) ) )
2022-02-03 02:20:20 -06:00
} , mock )
2017-02-08 07:20:07 -06:00
2022-01-14 10:55:57 -06:00
loggedInUserScenario ( t , "When calling GET on" , "/api/users/search" , "/api/users/search" , func ( sc * scenarioContext ) {
2022-08-16 07:24:57 -05:00
userMock . ExpectedSearchUsers = mockResult
2017-02-08 07:20:07 -06:00
2022-08-16 07:24:57 -05:00
searchUsersService := searchusers . ProvideUsersService ( filters . ProvideOSSSearchUserFilter ( ) , userMock )
2021-09-29 05:51:49 -05:00
sc . handlerFunc = searchUsersService . SearchUsersWithPaging
2020-11-13 02:52:38 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . exec ( )
2017-02-08 07:20:07 -06:00
2020-11-13 02:52:38 -06:00
respJSON , err := simplejson . NewJson ( sc . resp . Body . Bytes ( ) )
require . NoError ( t , err )
2017-02-08 07:20:07 -06:00
2022-03-02 04:05:31 -06:00
assert . Equal ( t , 1 , respJSON . Get ( "page" ) . MustInt ( ) )
assert . Equal ( t , 1000 , respJSON . Get ( "perPage" ) . MustInt ( ) )
2020-11-13 02:52:38 -06:00
assert . Equal ( t , 2 , respJSON . Get ( "totalCount" ) . MustInt ( ) )
assert . Equal ( t , 2 , len ( respJSON . Get ( "users" ) . MustArray ( ) ) )
2022-02-03 02:20:20 -06:00
} , mock )
2017-02-08 07:20:07 -06:00
2022-01-14 10:55:57 -06:00
loggedInUserScenario ( t , "When calling GET with page and perpage querystring parameters on" , "/api/users/search" , "/api/users/search" , func ( sc * scenarioContext ) {
2022-08-16 07:24:57 -05:00
userMock . ExpectedSearchUsers = mockResult
2017-02-08 07:20:07 -06:00
2022-08-16 07:24:57 -05:00
searchUsersService := searchusers . ProvideUsersService ( filters . ProvideOSSSearchUserFilter ( ) , userMock )
2021-09-29 05:51:49 -05:00
sc . handlerFunc = searchUsersService . SearchUsersWithPaging
2020-11-13 02:52:38 -06:00
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { "perpage" : "10" , "page" : "2" } ) . exec ( )
2017-02-08 07:20:07 -06:00
2022-03-02 04:05:31 -06:00
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 ( ) )
2022-02-03 02:20:20 -06:00
} , mock )
2017-02-08 07:20:07 -06:00
}
2022-11-14 06:11:26 -06:00
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
2023-01-27 01:50:36 -06:00
sc . defaultHandler = routing . Wrap ( func ( c * contextmodel . ReqContext ) response . Response {
2022-11-14 06:11:26 -06:00
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
2023-01-27 01:50:36 -06:00
sc . defaultHandler = routing . Wrap ( func ( c * contextmodel . ReqContext ) response . Response {
2022-11-14 06:11:26 -06:00
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 )
} )
}