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"
2024-02-16 11:54:59 -06:00
"net/url"
"strings"
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
2024-02-16 11:54:59 -06:00
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/notifications"
"github.com/grafana/grafana/pkg/services/secrets/fakes"
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
"github.com/grafana/grafana/pkg/services/temp_user/tempuserimpl"
"github.com/grafana/grafana/pkg/web/webtest"
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"
2024-01-23 08:26:38 -06:00
"github.com/grafana/grafana/pkg/infra/remotecache"
2023-08-01 10:39:08 -05:00
"github.com/grafana/grafana/pkg/login/social"
2023-12-08 04:20:42 -06:00
"github.com/grafana/grafana/pkg/login/social/socialtest"
2023-05-22 11:41:53 -05:00
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
2022-02-18 04:27:00 -06:00
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"
2023-01-27 12:36:54 -06:00
"github.com/grafana/grafana/pkg/services/login"
2023-11-21 07:47:23 -06:00
"github.com/grafana/grafana/pkg/services/login/authinfoimpl"
"github.com/grafana/grafana/pkg/services/login/authinfotest"
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"
2023-02-06 10:50:03 -06:00
"github.com/grafana/grafana/pkg/services/supportbundles/supportbundlestest"
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
)
2024-02-16 11:54:59 -06:00
const newEmail = "newEmail@localhost"
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 )
2023-05-22 11:41:53 -05:00
sqlStore . Cfg = settings
2022-01-26 13:24:05 -06:00
hs := & HTTPServer {
2022-02-18 04:27:00 -06:00
Cfg : settings ,
SQLStore : sqlStore ,
2023-05-22 11:41:53 -05:00
AccessControl : acimpl . ProvideAccessControl ( settings ) ,
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 ( )
2023-05-22 11:41:53 -05:00
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 ) )
2023-11-21 07:47:23 -06:00
authInfoStore := authinfoimpl . ProvideStore ( sqlStore , secretsService )
srv := authinfoimpl . ProvideService (
2024-01-23 08:26:38 -06:00
authInfoStore , remotecache . NewFakeCacheStorage ( ) , secretsService )
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 )
2023-02-06 10:50:03 -06:00
userSvc , err := userimpl . ProvideService ( sqlStore , orgSvc , sc . cfg , nil , nil , quotatest . New ( false , nil ) , supportbundlestest . NewFakeBundleService ( ) )
2022-12-07 10:03:22 -06:00
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-03-03 10:01:23 -06:00
usr , err := userSvc . Create ( context . Background ( ) , & createUserCmd )
2022-11-14 13:08:10 -06:00
require . NoError ( t , err )
2024-02-01 20:14:10 -06:00
theUserUID := usr . UID
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"
2023-08-30 10:46:47 -05:00
token = token . WithExtra ( map [ string ] any { "id_token" : idToken } )
2023-01-27 12:36:54 -06:00
userlogin := "loginuser"
query := & login . GetUserByAuthInfoQuery { AuthModule : "test" , AuthId : "test" , UserLookupParams : login . UserLookupParams { Login : & userlogin } }
cmd := & login . 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 )
2024-01-23 05:36:22 -06:00
avatarUrl := dtos . GetGravatarUrl ( hs . Cfg , "@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 ,
2024-02-01 20:14:10 -06:00
UID : theUserUID , // from original request
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 ,
2023-01-31 04:04:55 -06:00
AvatarURL : avatarUrl ,
2022-01-26 13:24:05 -06:00
}
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
2023-01-31 04:04:55 -06:00
resp . AvatarURL = avatarUrl
2022-01-26 13:24:05 -06:00
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 )
2023-02-06 10:50:03 -06:00
userSvc , err := userimpl . ProvideService ( sqlStore , orgSvc , sc . cfg , nil , nil , quotatest . New ( false , nil ) , supportbundlestest . NewFakeBundleService ( ) )
2022-12-07 10:03:22 -06:00
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
2024-01-23 05:36:22 -06:00
searchUsersService := searchusers . ProvideUsersService ( sc . cfg , 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
2024-01-23 05:36:22 -06:00
searchUsersService := searchusers . ProvideUsersService ( sc . cfg , 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
2024-01-23 05:36:22 -06:00
searchUsersService := searchusers . ProvideUsersService ( sc . cfg , 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
2024-01-23 05:36:22 -06:00
searchUsersService := searchusers . ProvideUsersService ( sc . cfg , 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
2023-08-01 10:39:08 -05:00
func Test_GetUserByID ( t * testing . T ) {
testcases := [ ] struct {
name string
authModule string
allowAssignGrafanaAdmin bool
authEnabled bool
skipOrgRoleSync bool
expectedIsGrafanaAdminSynced bool
} {
{
name : "Should return IsGrafanaAdminExternallySynced = false for an externally synced OAuth user if Grafana Admin role is not synced" ,
authModule : login . GenericOAuthModule ,
authEnabled : true ,
allowAssignGrafanaAdmin : false ,
skipOrgRoleSync : false ,
expectedIsGrafanaAdminSynced : false ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = false for an externally synced OAuth user if OAuth provider is not enabled" ,
authModule : login . GenericOAuthModule ,
authEnabled : false ,
allowAssignGrafanaAdmin : true ,
skipOrgRoleSync : false ,
expectedIsGrafanaAdminSynced : false ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = false for an externally synced OAuth user if org roles are not being synced" ,
authModule : login . GenericOAuthModule ,
authEnabled : true ,
allowAssignGrafanaAdmin : true ,
skipOrgRoleSync : true ,
expectedIsGrafanaAdminSynced : false ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = true for an externally synced OAuth user" ,
authModule : login . GenericOAuthModule ,
authEnabled : true ,
allowAssignGrafanaAdmin : true ,
skipOrgRoleSync : false ,
expectedIsGrafanaAdminSynced : true ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = false for an externally synced JWT user if Grafana Admin role is not synced" ,
authModule : login . JWTModule ,
authEnabled : true ,
allowAssignGrafanaAdmin : false ,
skipOrgRoleSync : false ,
expectedIsGrafanaAdminSynced : false ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = false for an externally synced JWT user if JWT provider is not enabled" ,
authModule : login . JWTModule ,
authEnabled : false ,
allowAssignGrafanaAdmin : true ,
skipOrgRoleSync : false ,
expectedIsGrafanaAdminSynced : false ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = false for an externally synced JWT user if org roles are not being synced" ,
authModule : login . JWTModule ,
authEnabled : true ,
allowAssignGrafanaAdmin : true ,
skipOrgRoleSync : true ,
expectedIsGrafanaAdminSynced : false ,
} ,
{
name : "Should return IsGrafanaAdminExternallySynced = true for an externally synced JWT user" ,
authModule : login . JWTModule ,
authEnabled : true ,
allowAssignGrafanaAdmin : true ,
skipOrgRoleSync : false ,
expectedIsGrafanaAdminSynced : true ,
} ,
}
for _ , tc := range testcases {
t . Run ( tc . name , func ( t * testing . T ) {
userAuth := & login . UserAuth { AuthModule : tc . authModule }
2023-11-21 07:47:23 -06:00
authInfoService := & authinfotest . FakeService { ExpectedUserAuth : userAuth }
2023-08-01 10:39:08 -05:00
socialService := & socialtest . FakeSocialService { }
userService := & usertest . FakeUserService { ExpectedUserProfileDTO : & user . UserProfileDTO { } }
cfg := setting . NewCfg ( )
switch tc . authModule {
case login . GenericOAuthModule :
2023-12-15 03:58:08 -06:00
socialService . ExpectedAuthInfoProvider = & social . OAuthInfo { AllowAssignGrafanaAdmin : tc . allowAssignGrafanaAdmin , Enabled : tc . authEnabled , SkipOrgRoleSync : tc . skipOrgRoleSync }
2023-08-01 10:39:08 -05:00
case login . JWTModule :
2024-02-09 09:35:58 -06:00
cfg . JWTAuth . Enabled = tc . authEnabled
cfg . JWTAuth . SkipOrgRoleSync = tc . skipOrgRoleSync
cfg . JWTAuth . AllowAssignGrafanaAdmin = tc . allowAssignGrafanaAdmin
2023-08-01 10:39:08 -05:00
}
hs := & HTTPServer {
Cfg : cfg ,
authInfoService : authInfoService ,
SocialService : socialService ,
userService : userService ,
}
sc := setupScenarioContext ( t , "/api/users/1" )
sc . defaultHandler = routing . Wrap ( func ( c * contextmodel . ReqContext ) response . Response {
sc . context = c
return hs . GetUserByID ( c )
} )
sc . m . Get ( "/api/users/:id" , sc . defaultHandler )
sc . fakeReqWithParams ( "GET" , sc . url , map [ string ] string { } ) . 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 )
assert . Equal ( t , tc . expectedIsGrafanaAdminSynced , resp . IsGrafanaAdminExternallySynced )
} )
}
}
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 ) {
2023-01-27 12:36:54 -06:00
sc . authInfoService . ExpectedUserAuth = & login . UserAuth { }
2022-11-14 06:11:26 -06:00
sc . fakeReqWithParams ( "PUT" , sc . url , map [ string ] string { "id" : "1" } ) . exec ( )
assert . Equal ( t , 403 , sc . resp . Code )
} ,
} , hs )
}
2024-02-16 11:54:59 -06:00
func setupUpdateEmailTests ( t * testing . T , cfg * setting . Cfg ) ( * user . User , * HTTPServer , * notifications . NotificationServiceMock ) {
t . Helper ( )
sqlStore := db . InitTestDB ( t )
sqlStore . Cfg = cfg
tempUserService := tempuserimpl . ProvideService ( sqlStore , cfg )
orgSvc , err := orgimpl . ProvideService ( sqlStore , cfg , quotatest . New ( false , nil ) )
require . NoError ( t , err )
userSvc , err := userimpl . ProvideService ( sqlStore , orgSvc , cfg , nil , nil , quotatest . New ( false , nil ) , supportbundlestest . NewFakeBundleService ( ) )
require . NoError ( t , err )
// Create test user
createUserCmd := user . CreateUserCommand {
Email : "testuser@localhost" ,
Name : "testuser" ,
Login : "loginuser" ,
Company : "testCompany" ,
IsAdmin : true ,
}
usr , err := userSvc . Create ( context . Background ( ) , & createUserCmd )
require . NoError ( t , err )
nsMock := notifications . MockNotificationService ( )
2024-03-14 07:25:28 -05:00
verifier := userimpl . ProvideVerifier ( userSvc , tempUserService , nsMock )
2024-02-16 11:54:59 -06:00
hs := & HTTPServer {
Cfg : cfg ,
SQLStore : sqlStore ,
userService : userSvc ,
tempUserService : tempUserService ,
NotificationService : nsMock ,
2024-03-14 07:25:28 -05:00
userVerifier : verifier ,
2024-02-16 11:54:59 -06:00
}
return usr , hs , nsMock
}
func TestUser_UpdateEmail ( t * testing . T ) {
cases := [ ] struct {
Name string
Field user . UpdateEmailActionType
} {
{
Name : "Updating Email field" ,
Field : user . EmailUpdateAction ,
} ,
{
Name : "Updating Login (username) field" ,
Field : user . LoginUpdateAction ,
} ,
}
for _ , tt := range cases {
t . Run ( tt . Name , func ( t * testing . T ) {
t . Run ( "With verification disabled should update without verifying" , func ( t * testing . T ) {
tests := [ ] struct {
name string
smtpConfigured bool
verifyEmailEnabled bool
} {
{
name : "SMTP not configured" ,
smtpConfigured : false ,
verifyEmailEnabled : true ,
} ,
{
name : "config verify_email_enabled = false" ,
smtpConfigured : true ,
verifyEmailEnabled : false ,
} ,
{
name : "config verify_email_enabled = false and SMTP not configured" ,
smtpConfigured : false ,
verifyEmailEnabled : false ,
} ,
}
for _ , ttt := range tests {
settings := setting . NewCfg ( )
settings . Smtp . Enabled = ttt . smtpConfigured
settings . VerifyEmailEnabled = ttt . verifyEmailEnabled
usr , hs , nsMock := setupUpdateEmailTests ( t , settings )
updateUserCommand := user . UpdateUserCommand {
Email : usr . Email ,
Name : "newName" ,
Login : usr . Login ,
UserID : usr . ID ,
}
switch tt . Field {
case user . LoginUpdateAction :
updateUserCommand . Login = newEmail
case user . EmailUpdateAction :
updateUserCommand . Email = newEmail
}
fn := func ( sc * scenarioContext ) {
// User is internal
sc . authInfoService . ExpectedError = user . ErrUserNotFound
sc . fakeReqWithParams ( "PUT" , sc . url , nil ) . exec ( )
assert . Equal ( t , http . StatusOK , sc . resp . Code )
// Verify that no email has been sent after update
require . False ( t , nsMock . EmailVerified )
userQuery := user . GetUserByIDQuery { ID : usr . ID }
updatedUsr , err := hs . userService . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
// Verify fields have been updated
require . NotEqual ( t , usr . Name , updatedUsr . Name )
require . Equal ( t , updateUserCommand . Name , updatedUsr . Name )
switch tt . Field {
case user . LoginUpdateAction :
require . Equal ( t , usr . Email , updatedUsr . Email )
require . NotEqual ( t , usr . Login , updatedUsr . Login )
require . Equal ( t , updateUserCommand . Login , updatedUsr . Login )
case user . EmailUpdateAction :
require . Equal ( t , usr . Login , updatedUsr . Login )
require . NotEqual ( t , usr . Email , updatedUsr . Email )
require . Equal ( t , updateUserCommand . Email , updatedUsr . Email )
}
// Verify other fields have been kept
require . Equal ( t , usr . Company , updatedUsr . Company )
}
updateUserScenario ( t , updateUserContext {
desc : ttt . name ,
url : fmt . Sprintf ( "/api/users/%d" , usr . ID ) ,
routePattern : "/api/users/:id" ,
cmd : updateUserCommand ,
fn : fn ,
} , hs )
updateSignedInUserScenario ( t , updateUserContext {
desc : ttt . name ,
url : "/api/user" ,
routePattern : "/api/user" ,
cmd : updateUserCommand ,
fn : fn ,
} , hs )
}
} )
} )
}
doReq := func ( req * http . Request , usr * user . User ) ( * http . Response , error ) {
r := webtest . RequestWithSignedInUser (
req ,
authedUserWithPermissions (
usr . ID ,
usr . OrgID ,
[ ] accesscontrol . Permission {
{
Action : accesscontrol . ActionUsersWrite ,
Scope : accesscontrol . ScopeGlobalUsersAll ,
} ,
} ,
) ,
)
client := & http . Client {
CheckRedirect : func ( req * http . Request , via [ ] * http . Request ) error {
return http . ErrUseLastResponse
} }
return client . Do ( r )
}
sendUpdateReq := func ( server * webtest . Server , usr * user . User , body string ) {
req := server . NewRequest (
http . MethodPut ,
"/api/user" ,
strings . NewReader ( body ) ,
)
req . Header . Add ( "Content-Type" , "application/json" )
res , err := doReq ( req , usr )
require . NoError ( t , err )
assert . Equal ( t , http . StatusOK , res . StatusCode )
require . NoError ( t , res . Body . Close ( ) )
}
sendVerificationReq := func ( server * webtest . Server , usr * user . User , code string ) {
url := fmt . Sprintf ( "/user/email/update?code=%s" , url . QueryEscape ( code ) )
req := server . NewGetRequest ( url )
res , err := doReq ( req , usr )
require . NoError ( t , err )
assert . Equal ( t , http . StatusFound , res . StatusCode )
require . NoError ( t , res . Body . Close ( ) )
}
getVerificationTempUser := func ( tempUserSvc tempuser . Service , code string ) * tempuser . TempUserDTO {
tmpUserQuery := tempuser . GetTempUserByCodeQuery { Code : code }
tmpUser , err := tempUserSvc . GetTempUserByCode ( context . Background ( ) , & tmpUserQuery )
require . NoError ( t , err )
return tmpUser
}
verifyEmailData := func ( tempUserSvc tempuser . Service , nsMock * notifications . NotificationServiceMock , originalUsr * user . User , newEmail string ) {
verification := nsMock . EmailVerification
tmpUsr := getVerificationTempUser ( tempUserSvc , verification . Code )
require . True ( t , nsMock . EmailVerified )
require . Equal ( t , newEmail , verification . Email )
require . Equal ( t , originalUsr . ID , verification . User . ID )
require . Equal ( t , tmpUsr . Code , verification . Code )
}
verifyUserNotUpdated := func ( userSvc user . Service , usr * user . User ) {
userQuery := user . GetUserByIDQuery { ID : usr . ID }
checkUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . Equal ( t , usr . Email , checkUsr . Email )
require . Equal ( t , usr . Login , checkUsr . Login )
require . Equal ( t , usr . Name , checkUsr . Name )
}
setupScenario := func ( cfg * setting . Cfg ) ( * webtest . Server , user . Service , tempuser . Service , * notifications . NotificationServiceMock ) {
settings := setting . NewCfg ( )
settings . Smtp . Enabled = true
settings . VerificationEmailMaxLifetime = 1 * time . Hour
settings . VerifyEmailEnabled = true
if cfg != nil {
settings = cfg
}
nsMock := notifications . MockNotificationService ( )
sqlStore := db . InitTestDB ( t )
sqlStore . Cfg = settings
tempUserSvc := tempuserimpl . ProvideService ( sqlStore , settings )
orgSvc , err := orgimpl . ProvideService ( sqlStore , settings , quotatest . New ( false , nil ) )
require . NoError ( t , err )
userSvc , err := userimpl . ProvideService ( sqlStore , orgSvc , settings , nil , nil , quotatest . New ( false , nil ) , supportbundlestest . NewFakeBundleService ( ) )
require . NoError ( t , err )
server := SetupAPITestServer ( t , func ( hs * HTTPServer ) {
hs . Cfg = settings
hs . SQLStore = sqlStore
hs . userService = userSvc
hs . tempUserService = tempUserSvc
hs . NotificationService = nsMock
hs . SecretsService = fakes . NewFakeSecretsService ( )
2024-03-14 07:25:28 -05:00
hs . userVerifier = userimpl . ProvideVerifier ( userSvc , tempUserSvc , nsMock )
2024-02-16 11:54:59 -06:00
// User is internal
hs . authInfoService = & authinfotest . FakeService { ExpectedError : user . ErrUserNotFound }
} )
return server , userSvc , tempUserSvc , nsMock
}
createUser := func ( userSvc user . Service , name string , email string , login string ) * user . User {
createUserCmd := user . CreateUserCommand {
Email : email ,
Name : name ,
Login : login ,
Company : "testCompany" ,
IsAdmin : true ,
}
usr , err := userSvc . Create ( context . Background ( ) , & createUserCmd )
require . NoError ( t , err )
return usr
}
t . Run ( "Update Email and disregard other fields" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
newName := "newName"
body := fmt . Sprintf ( ` { "email": "%s", "name": "%s"} ` , newEmail , newName )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow, when user clicks email button
code := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , code )
// Verify Email has been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Email , updatedUsr . Email )
require . Equal ( t , newEmail , updatedUsr . Email )
// Fields unchanged
require . Equal ( t , originalUsr . Login , updatedUsr . Login )
require . Equal ( t , originalUsr . Name , updatedUsr . Name )
require . NotEqual ( t , newName , updatedUsr . Name )
} )
t . Run ( "Update Email when Login was also an email should update both" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "email@localhost" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
body := fmt . Sprintf ( ` { "email": "%s"} ` , newEmail )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow, when user clicks email button
code := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , code )
// Verify Email and Login have been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Email , updatedUsr . Email )
require . Equal ( t , newEmail , updatedUsr . Email )
require . Equal ( t , newEmail , updatedUsr . Login )
// Fields unchanged
require . Equal ( t , originalUsr . Name , updatedUsr . Name )
} )
t . Run ( "Update Login with an email should update Email too" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
body := fmt . Sprintf ( ` { "login": "%s"} ` , newEmail )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow, when user clicks email button
code := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , code )
// Verify Email and Login have been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Email , updatedUsr . Email )
require . NotEqual ( t , originalUsr . Login , updatedUsr . Login )
require . Equal ( t , newEmail , updatedUsr . Email )
require . Equal ( t , newEmail , updatedUsr . Login )
// Fields unchanged
require . Equal ( t , originalUsr . Name , updatedUsr . Name )
} )
t . Run ( "Update Login should not need verification if it is not an email" , func ( t * testing . T ) {
server , userSvc , _ , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
newLogin := "newLogin"
newName := "newName"
body := fmt . Sprintf ( ` { "login": "%s", "name": "%s"} ` , newLogin , newName )
sendUpdateReq ( server , originalUsr , body )
// Verify that email has not been sent
require . False ( t , nsMock . EmailVerified )
// Verify Login has been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Login , updatedUsr . Login )
require . NotEqual ( t , originalUsr . Name , updatedUsr . Name )
require . Equal ( t , newLogin , updatedUsr . Login )
require . Equal ( t , newName , updatedUsr . Name )
// Fields unchanged
require . Equal ( t , originalUsr . Email , updatedUsr . Email )
} )
t . Run ( "Update Login should not need verification if it is being updated to the already configured email" , func ( t * testing . T ) {
server , userSvc , _ , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
body := fmt . Sprintf ( ` { "login": "%s"} ` , originalUsr . Email )
sendUpdateReq ( server , originalUsr , body )
// Verify that email has not been sent
require . False ( t , nsMock . EmailVerified )
// Verify Login has been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Login , updatedUsr . Login )
require . Equal ( t , originalUsr . Email , updatedUsr . Login )
require . Equal ( t , originalUsr . Email , updatedUsr . Email )
} )
t . Run ( "Update Login and Email with different email values at once should disregard the Login update" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
newLogin := "newEmail2@localhost"
body := fmt . Sprintf ( ` { "email": "%s", "login": "%s"} ` , newEmail , newLogin )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow, when user clicks email button
code := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , code )
// Verify only Email has been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Email , updatedUsr . Email )
require . Equal ( t , newEmail , updatedUsr . Email )
// Fields unchanged
require . NotEqual ( t , newLogin , updatedUsr . Login )
require . Equal ( t , originalUsr . Login , updatedUsr . Login )
require . Equal ( t , originalUsr . Name , updatedUsr . Name )
} )
t . Run ( "Update Login and Email with different email values at once when Login was already an email should update both with Email" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "email@localhost" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
newLogin := "newEmail2@localhost"
body := fmt . Sprintf ( ` { "email": "%s", "login": "%s"} ` , newEmail , newLogin )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow, when user clicks email button
code := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , code )
// Verify only Email has been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Email , updatedUsr . Email )
require . NotEqual ( t , originalUsr . Login , updatedUsr . Login )
require . NotEqual ( t , newLogin , updatedUsr . Login )
require . Equal ( t , newEmail , updatedUsr . Email )
require . Equal ( t , newEmail , updatedUsr . Login )
// Fields unchanged
require . Equal ( t , originalUsr . Name , updatedUsr . Name )
} )
t . Run ( "Email verification should expire" , func ( t * testing . T ) {
cfg := setting . NewCfg ( )
cfg . Smtp . Enabled = true
cfg . VerificationEmailMaxLifetime = 0 // Expire instantly
cfg . VerifyEmailEnabled = true
server , userSvc , tempUserSvc , nsMock := setupScenario ( cfg )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
body := fmt . Sprintf ( ` { "email": "%s"} ` , newEmail )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow, when user clicks email button
code := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , code )
// Verify user has not been updated
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , newEmail , updatedUsr . Email )
require . Equal ( t , originalUsr . Email , updatedUsr . Email )
require . Equal ( t , originalUsr . Login , updatedUsr . Login )
} )
t . Run ( "A new verification should revoke other pending verifications" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// First email verification
firstNewEmail := "newEmail1@localhost"
body := fmt . Sprintf ( ` { "email": "%s"} ` , firstNewEmail )
sendUpdateReq ( server , originalUsr , body )
verifyEmailData ( tempUserSvc , nsMock , originalUsr , firstNewEmail )
firstCode := nsMock . EmailVerification . Code
// Second email verification
secondNewEmail := "newEmail2@localhost"
body = fmt . Sprintf ( ` { "email": "%s"} ` , secondNewEmail )
sendUpdateReq ( server , originalUsr , body )
verifyEmailData ( tempUserSvc , nsMock , originalUsr , secondNewEmail )
secondCode := nsMock . EmailVerification . Code
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Try to follow through with the first verification unsuccessfully
sendVerificationReq ( server , originalUsr , firstCode )
verifyUserNotUpdated ( userSvc , originalUsr )
// Follow through with second verification successfully
sendVerificationReq ( server , originalUsr , secondCode )
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . NotEqual ( t , originalUsr . Email , updatedUsr . Email )
require . Equal ( t , secondNewEmail , updatedUsr . Email )
// Fields unchanged
require . Equal ( t , originalUsr . Login , updatedUsr . Login )
} )
t . Run ( "Email verification should fail if code is not valid" , func ( t * testing . T ) {
server , userSvc , tempUserSvc , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Start email update
body := fmt . Sprintf ( ` { "email": "%s"} ` , newEmail )
sendUpdateReq ( server , originalUsr , body )
// Verify email data
verifyEmailData ( tempUserSvc , nsMock , originalUsr , newEmail )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Second part of the verification flow should fail if using the wrong code
sendVerificationReq ( server , originalUsr , "notTheRightCode" )
verifyUserNotUpdated ( userSvc , originalUsr )
} )
t . Run ( "Email verification code can only be used once" , func ( t * testing . T ) {
server , userSvc , _ , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name" , "email@localhost" , "login" )
// Start email update
require . NotEqual ( t , originalUsr . Email , newEmail )
body := fmt . Sprintf ( ` { "email": "%s"} ` , newEmail )
sendUpdateReq ( server , originalUsr , body )
// Verify user has not been updated yet
verifyUserNotUpdated ( userSvc , originalUsr )
// Use code to verify successfully
codeToReuse := nsMock . EmailVerification . Code
sendVerificationReq ( server , originalUsr , codeToReuse )
// User should have an updated Email
userQuery := user . GetUserByIDQuery { ID : originalUsr . ID }
updatedUsr , err := userSvc . GetByID ( context . Background ( ) , & userQuery )
require . NoError ( t , err )
require . Equal ( t , newEmail , updatedUsr . Email )
// Change email back to what it was
body = fmt . Sprintf ( ` { "email": "%s"} ` , originalUsr . Email )
sendUpdateReq ( server , originalUsr , body )
sendVerificationReq ( server , originalUsr , nsMock . EmailVerification . Code )
verifyUserNotUpdated ( userSvc , originalUsr )
// Re-use code to verify new email again, unsuccessfully
sendVerificationReq ( server , originalUsr , codeToReuse )
verifyUserNotUpdated ( userSvc , originalUsr )
} )
t . Run ( "Update Email with an email that is already being used should fail" , func ( t * testing . T ) {
testCases := [ ] struct {
description string
clashLogin bool
} {
{
description : "when Email clashes" ,
clashLogin : false ,
} ,
{
description : "when Login clashes" ,
clashLogin : true ,
} ,
}
for _ , tt := range testCases {
t . Run ( tt . description , func ( t * testing . T ) {
server , userSvc , _ , nsMock := setupScenario ( nil )
originalUsr := createUser ( userSvc , "name1" , "email1@localhost" , "login1@localhost" )
badUsr := createUser ( userSvc , "name2" , "email2@localhost" , "login2" )
// Verify that no email has been sent yet
require . False ( t , nsMock . EmailVerified )
// Update `badUsr` to use the same email as `originalUsr`
body := fmt . Sprintf ( ` { "email": "%s"} ` , originalUsr . Email )
if tt . clashLogin {
body = fmt . Sprintf ( ` { "login": "%s"} ` , originalUsr . Login )
}
req := server . NewRequest (
http . MethodPut ,
"/api/user" ,
strings . NewReader ( body ) ,
)
req . Header . Add ( "Content-Type" , "application/json" )
res , err := doReq ( req , badUsr )
require . NoError ( t , err )
assert . Equal ( t , http . StatusConflict , res . StatusCode )
require . NoError ( t , res . Body . Close ( ) )
// Verify that no email has been sent
require . False ( t , nsMock . EmailVerified )
// Verify user has not been updated
verifyUserNotUpdated ( userSvc , badUsr )
} )
}
} )
}
2022-11-14 06:11:26 -06:00
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 )
2023-11-21 07:47:23 -06:00
sc . authInfoService = & authinfotest . FakeService { }
2022-11-14 06:11:26 -06:00
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 ) {
2023-01-27 12:36:54 -06:00
sc . authInfoService . ExpectedUserAuth = & login . UserAuth { }
2022-11-14 06:11:26 -06:00
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 )
2023-11-21 07:47:23 -06:00
sc . authInfoService = & authinfotest . FakeService { }
2022-11-14 06:11:26 -06:00
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 )
} )
}