2018-12-01 11:28:10 -06:00
package api
import (
"testing"
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
2019-05-21 06:52:49 -05:00
"github.com/grafana/grafana/pkg/components/simplejson"
2020-03-04 05:57:20 -06:00
"github.com/grafana/grafana/pkg/models"
2019-03-08 08:15:38 -06:00
"github.com/grafana/grafana/pkg/services/auth"
2018-12-01 11:28:10 -06:00
. "github.com/smartystreets/goconvey/convey"
)
2020-04-15 04:11:45 -05:00
const (
TestLogin = "test@example.com"
TestPassword = "password"
nonExistingOrgID = 1000
)
2018-12-01 11:28:10 -06:00
func TestAdminApiEndpoint ( t * testing . T ) {
2020-03-04 05:57:20 -06:00
role := models . ROLE_ADMIN
2018-12-01 11:28:10 -06:00
Convey ( "Given a server admin attempts to remove themself as an admin" , t , func ( ) {
updateCmd := dtos . AdminUpdateUserPermissionsForm {
IsGrafanaAdmin : false ,
}
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . UpdateUserPermissionsCommand ) error {
return models . ErrLastGrafanaAdmin
2018-12-01 11:28:10 -06:00
} )
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 )
} )
} )
2019-03-08 08:15:38 -06:00
Convey ( "When a server admin attempts to logout himself from all devices" , t , func ( ) {
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . GetUserByIdQuery ) error {
cmd . Result = & models . User { Id : TestUserID }
2019-03-08 08:15:38 -06:00
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 )
} )
} )
Convey ( "When a server admin attempts to logout a non-existing user from all devices" , t , func ( ) {
userId := int64 ( 0 )
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . GetUserByIdQuery ) error {
2019-03-08 08:15:38 -06:00
userId = cmd . Id
2020-03-04 05:57:20 -06:00
return models . ErrUserNotFound
2019-03-08 08:15:38 -06:00
} )
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 )
} )
} )
Convey ( "When a server admin attempts to revoke an auth token for a non-existing user" , t , func ( ) {
userId := int64 ( 0 )
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . GetUserByIdQuery ) error {
2019-03-08 08:15:38 -06:00
userId = cmd . Id
2020-03-04 05:57:20 -06:00
return models . ErrUserNotFound
2019-03-08 08:15:38 -06:00
} )
2020-03-04 05:57:20 -06:00
cmd := models . RevokeAuthTokenCmd { AuthTokenId : 2 }
2019-03-08 08:15:38 -06:00
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 )
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . GetUserByIdQuery ) error {
2019-03-08 08:15:38 -06:00
userId = cmd . Id
2020-03-04 05:57:20 -06:00
return models . ErrUserNotFound
2019-03-08 08:15:38 -06:00
} )
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 )
} )
} )
2019-05-21 06:52:49 -05:00
2020-01-10 04:43:44 -06:00
Convey ( "When a server admin attempts to enable/disable a nonexistent user" , t , func ( ) {
var userId int64
isDisabled := false
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . GetAuthInfoQuery ) error {
return models . ErrUserNotFound
2020-01-10 04:43:44 -06:00
} )
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . DisableUserCommand ) error {
2020-01-10 04:43:44 -06:00
userId = cmd . UserId
isDisabled = cmd . IsDisabled
2020-03-04 05:57:20 -06:00
return models . ErrUserNotFound
2020-01-10 04:43:44 -06:00
} )
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 )
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 , false )
} )
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 )
} )
} )
2019-05-21 06:52:49 -05:00
Convey ( "When a server admin attempts to disable/enable external user" , t , func ( ) {
userId := int64 ( 0 )
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . GetAuthInfoQuery ) error {
2019-05-21 06:52:49 -05:00
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 )
} )
} )
2020-01-10 04:43:44 -06:00
Convey ( "When a server admin attempts to delete a nonexistent user" , t , func ( ) {
var userId int64
2020-03-04 05:57:20 -06:00
bus . AddHandler ( "test" , func ( cmd * models . DeleteUserCommand ) error {
2020-01-10 04:43:44 -06:00
userId = cmd . UserId
2020-03-04 05:57:20 -06:00
return models . ErrUserNotFound
2020-01-10 04:43:44 -06:00
} )
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 ( )
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 )
} )
} )
2020-04-15 04:11:45 -05:00
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 }
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 )
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" )
// test that userLogin and orgId were transmitted correctly to the handler
So ( userLogin , ShouldEqual , TestLogin )
So ( orgId , ShouldEqual , 0 )
} )
} )
Convey ( "With an organization" , func ( ) {
createCmd := dtos . AdminCreateUserForm {
Login : TestLogin ,
Password : TestPassword ,
OrgId : TestOrgID ,
}
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 )
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" )
So ( userLogin , ShouldEqual , TestLogin )
So ( orgId , ShouldEqual , TestOrgID )
} )
} )
Convey ( "With a nonexistent organization" , func ( ) {
createCmd := dtos . AdminCreateUserForm {
Login : TestLogin ,
Password : TestPassword ,
OrgId : nonExistingOrgID ,
}
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 , 400 )
respJSON , err := simplejson . NewJson ( sc . resp . Body . Bytes ( ) )
So ( err , ShouldBeNil )
So ( respJSON . Get ( "message" ) . MustString ( ) , ShouldEqual , "Organization not found" )
So ( userLogin , ShouldEqual , TestLogin )
So ( orgId , ShouldEqual , 1000 )
} )
} )
} )
2020-08-13 07:38:54 -05:00
Convey ( "When a server admin attempts to create a user with an already existing email/login" , t , func ( ) {
bus . AddHandler ( "test" , func ( cmd * models . CreateUserCommand ) error {
return models . ErrUserAlreadyExists
} )
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 )
respJSON , err := simplejson . NewJson ( sc . resp . Body . Bytes ( ) )
So ( err , ShouldBeNil )
So ( respJSON . Get ( "error" ) . MustString ( ) , ShouldEqual , "User already exists" )
} )
} )
2018-12-01 11:28:10 -06:00
}
2020-03-04 05:57:20 -06:00
func putAdminScenario ( desc string , url string , routePattern string , role models . RoleType , cmd dtos . AdminUpdateUserPermissionsForm , fn scenarioFunc ) {
2018-12-01 11:28:10 -06:00
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
sc := setupScenarioContext ( url )
2020-07-29 11:00:14 -05:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2018-12-01 11:28:10 -06:00
sc . context = c
sc . context . UserId = TestUserID
sc . context . OrgId = TestOrgID
sc . context . OrgRole = role
2020-07-29 11:00:14 -05:00
return AdminUpdateUserPermissions ( c , cmd )
2018-12-01 11:28:10 -06:00
} )
sc . m . Put ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}
2019-03-08 08:15:38 -06:00
func adminLogoutUserScenario ( desc string , url string , routePattern string , fn scenarioFunc ) {
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
hs := HTTPServer {
Bus : bus . GetBus ( ) ,
AuthTokenService : auth . NewFakeUserAuthTokenService ( ) ,
}
sc := setupScenarioContext ( url )
2020-03-04 05:57:20 -06:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2019-03-08 08:15:38 -06:00
sc . context = c
sc . context . UserId = TestUserID
sc . context . OrgId = TestOrgID
2020-03-04 05:57:20 -06:00
sc . context . OrgRole = models . ROLE_ADMIN
2019-03-08 08:15:38 -06:00
return hs . AdminLogoutUser ( c )
} )
sc . m . Post ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}
2020-03-04 05:57:20 -06:00
func adminRevokeUserAuthTokenScenario ( desc string , url string , routePattern string , cmd models . RevokeAuthTokenCmd , fn scenarioFunc ) {
2019-03-08 08:15:38 -06:00
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
fakeAuthTokenService := auth . NewFakeUserAuthTokenService ( )
hs := HTTPServer {
Bus : bus . GetBus ( ) ,
AuthTokenService : fakeAuthTokenService ,
}
sc := setupScenarioContext ( url )
sc . userAuthTokenService = fakeAuthTokenService
2020-03-04 05:57:20 -06:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2019-03-08 08:15:38 -06:00
sc . context = c
sc . context . UserId = TestUserID
sc . context . OrgId = TestOrgID
2020-03-04 05:57:20 -06:00
sc . context . OrgRole = models . ROLE_ADMIN
2019-03-08 08:15:38 -06:00
return hs . AdminRevokeUserAuthToken ( c , cmd )
} )
sc . m . Post ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}
func adminGetUserAuthTokensScenario ( desc string , url string , routePattern string , fn scenarioFunc ) {
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
fakeAuthTokenService := auth . NewFakeUserAuthTokenService ( )
hs := HTTPServer {
Bus : bus . GetBus ( ) ,
AuthTokenService : fakeAuthTokenService ,
}
sc := setupScenarioContext ( url )
sc . userAuthTokenService = fakeAuthTokenService
2020-03-04 05:57:20 -06:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2019-03-08 08:15:38 -06:00
sc . context = c
sc . context . UserId = TestUserID
sc . context . OrgId = TestOrgID
2020-03-04 05:57:20 -06:00
sc . context . OrgRole = models . ROLE_ADMIN
2019-03-08 08:15:38 -06:00
return hs . AdminGetUserAuthTokens ( c )
} )
sc . m . Get ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}
2019-05-21 06:52:49 -05:00
func adminDisableUserScenario ( desc string , action string , url string , routePattern string , fn scenarioFunc ) {
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
2019-05-23 07:54:47 -05:00
fakeAuthTokenService := auth . NewFakeUserAuthTokenService ( )
hs := HTTPServer {
Bus : bus . GetBus ( ) ,
AuthTokenService : fakeAuthTokenService ,
}
2019-05-21 06:52:49 -05:00
sc := setupScenarioContext ( url )
2020-03-04 05:57:20 -06:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2019-05-21 06:52:49 -05:00
sc . context = c
sc . context . UserId = TestUserID
if action == "enable" {
2019-05-23 07:54:47 -05:00
return AdminEnableUser ( c )
2019-05-21 06:52:49 -05:00
}
2019-05-23 07:54:47 -05:00
return hs . AdminDisableUser ( c )
2019-05-21 06:52:49 -05:00
} )
sc . m . Post ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}
2020-01-10 04:43:44 -06:00
func adminDeleteUserScenario ( desc string , url string , routePattern string , fn scenarioFunc ) {
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
sc := setupScenarioContext ( url )
2020-07-29 11:00:14 -05:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2020-01-10 04:43:44 -06:00
sc . context = c
sc . context . UserId = TestUserID
2020-07-29 11:00:14 -05:00
return AdminDeleteUser ( c )
2020-01-10 04:43:44 -06:00
} )
sc . m . Delete ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}
2020-04-15 04:11:45 -05:00
func adminCreateUserScenario ( desc string , url string , routePattern string , cmd dtos . AdminCreateUserForm , fn scenarioFunc ) {
Convey ( desc + " " + url , func ( ) {
defer bus . ClearBusHandlers ( )
sc := setupScenarioContext ( url )
2020-07-29 11:00:14 -05:00
sc . defaultHandler = Wrap ( func ( c * models . ReqContext ) Response {
2020-04-15 04:11:45 -05:00
sc . context = c
sc . context . UserId = TestUserID
2020-07-29 11:00:14 -05:00
return AdminCreateUser ( c , cmd )
2020-04-15 04:11:45 -05:00
} )
sc . m . Post ( routePattern , sc . defaultHandler )
fn ( sc )
} )
}