mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
267 lines
9.0 KiB
Go
267 lines
9.0 KiB
Go
package userimpl
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
"github.com/grafana/grafana/pkg/apimachinery/identity"
|
|
"github.com/grafana/grafana/pkg/services/auth/idtest"
|
|
"github.com/grafana/grafana/pkg/services/notifications"
|
|
tempuser "github.com/grafana/grafana/pkg/services/temp_user"
|
|
"github.com/grafana/grafana/pkg/services/temp_user/tempusertest"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/services/user/usertest"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
)
|
|
|
|
func TestVerifier_Start(t *testing.T) {
|
|
ts := &tempusertest.FakeTempUserService{}
|
|
us := &usertest.FakeUserService{}
|
|
ns := notifications.MockNotificationService()
|
|
is := &idtest.MockService{}
|
|
|
|
type calls struct {
|
|
expireCalled bool
|
|
createCalled bool
|
|
updateCalled bool
|
|
}
|
|
|
|
verifier := ProvideVerifier(setting.NewCfg(), us, ts, ns, is)
|
|
t.Run("should error if email already exist for other user", func(t *testing.T) {
|
|
us.ExpectedUser = &user.User{ID: 1}
|
|
err := verifier.Start(context.Background(), user.StartVerifyEmailCommand{
|
|
User: user.User{ID: 2},
|
|
Email: "some@email.com",
|
|
Action: user.EmailUpdateAction,
|
|
})
|
|
|
|
assert.ErrorIs(t, err, user.ErrEmailConflict)
|
|
})
|
|
|
|
t.Run("should succeed when no user has the email", func(t *testing.T) {
|
|
us.ExpectedUser = nil
|
|
var c calls
|
|
ts.ExpirePreviousVerificationsFN = func(ctx context.Context, cmd *tempuser.ExpirePreviousVerificationsCommand) error {
|
|
c.expireCalled = true
|
|
return nil
|
|
}
|
|
|
|
ts.CreateTempUserFN = func(ctx context.Context, cmd *tempuser.CreateTempUserCommand) (*tempuser.TempUser, error) {
|
|
c.createCalled = true
|
|
return &tempuser.TempUser{
|
|
OrgID: cmd.OrgID,
|
|
Email: cmd.Email,
|
|
Name: cmd.Name,
|
|
InvitedByUserID: cmd.InvitedByUserID,
|
|
Code: cmd.Code,
|
|
}, nil
|
|
}
|
|
|
|
ts.UpdateTempUserWithEmailSentFN = func(ctx context.Context, cmd *tempuser.UpdateTempUserWithEmailSentCommand) error {
|
|
c.updateCalled = true
|
|
return nil
|
|
}
|
|
err := verifier.Start(context.Background(), user.StartVerifyEmailCommand{
|
|
User: user.User{ID: 2},
|
|
Email: "some@email.com",
|
|
Action: user.EmailUpdateAction,
|
|
})
|
|
|
|
assert.NoError(t, err)
|
|
assert.True(t, c.expireCalled)
|
|
assert.True(t, c.createCalled)
|
|
assert.True(t, c.updateCalled)
|
|
})
|
|
|
|
t.Run("should succeed when the user holding the email is the same user that want to verify it", func(t *testing.T) {
|
|
us.ExpectedUser = &user.User{ID: 2}
|
|
var c calls
|
|
ts.ExpirePreviousVerificationsFN = func(ctx context.Context, cmd *tempuser.ExpirePreviousVerificationsCommand) error {
|
|
c.expireCalled = true
|
|
return nil
|
|
}
|
|
|
|
ts.CreateTempUserFN = func(ctx context.Context, cmd *tempuser.CreateTempUserCommand) (*tempuser.TempUser, error) {
|
|
c.createCalled = true
|
|
return &tempuser.TempUser{
|
|
OrgID: cmd.OrgID,
|
|
Email: cmd.Email,
|
|
Name: cmd.Name,
|
|
InvitedByUserID: cmd.InvitedByUserID,
|
|
Code: cmd.Code,
|
|
}, nil
|
|
}
|
|
|
|
ts.UpdateTempUserWithEmailSentFN = func(ctx context.Context, cmd *tempuser.UpdateTempUserWithEmailSentCommand) error {
|
|
c.updateCalled = true
|
|
return nil
|
|
}
|
|
err := verifier.Start(context.Background(), user.StartVerifyEmailCommand{
|
|
User: user.User{ID: 2},
|
|
Email: "some@email.com",
|
|
Action: user.EmailUpdateAction,
|
|
})
|
|
|
|
assert.ErrorIs(t, err, nil)
|
|
assert.True(t, c.expireCalled)
|
|
assert.True(t, c.createCalled)
|
|
assert.True(t, c.updateCalled)
|
|
})
|
|
}
|
|
|
|
func TestVerifier_Complete(t *testing.T) {
|
|
ts := &tempusertest.FakeTempUserService{}
|
|
us := &usertest.FakeUserService{}
|
|
ns := notifications.MockNotificationService()
|
|
is := &idtest.MockService{}
|
|
|
|
type calls struct {
|
|
updateCalled bool
|
|
updateStatusCalled bool
|
|
removeTokenCalled bool
|
|
}
|
|
|
|
cfg := setting.NewCfg()
|
|
cfg.VerificationEmailMaxLifetime = 1 * time.Hour
|
|
verifier := ProvideVerifier(cfg, us, ts, ns, is)
|
|
t.Run("should return error for invalid code", func(t *testing.T) {
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return nil, tempuser.ErrTempUserNotFound
|
|
}
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.ErrorIs(t, err, errInvalidCode)
|
|
})
|
|
|
|
t.Run("should return error when verification has wrong status", func(t *testing.T) {
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return &tempuser.TempUserDTO{
|
|
Status: tempuser.TmpUserEmailUpdateCompleted,
|
|
}, nil
|
|
}
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.ErrorIs(t, err, errInvalidCode)
|
|
})
|
|
|
|
t.Run("should return error when verification email was never sent", func(t *testing.T) {
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return &tempuser.TempUserDTO{
|
|
Status: tempuser.TmpUserEmailUpdateStarted,
|
|
EmailSent: false,
|
|
}, nil
|
|
}
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.ErrorIs(t, err, errInvalidCode)
|
|
})
|
|
|
|
t.Run("should return error when verification code has expired", func(t *testing.T) {
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return &tempuser.TempUserDTO{
|
|
Status: tempuser.TmpUserEmailUpdateStarted,
|
|
EmailSent: true,
|
|
EmailSentOn: time.Now().Add(-10 * time.Hour),
|
|
}, nil
|
|
}
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.ErrorIs(t, err, errExpiredCode)
|
|
})
|
|
|
|
t.Run("should return error user connect to code don't exists", func(t *testing.T) {
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return &tempuser.TempUserDTO{
|
|
Status: tempuser.TmpUserEmailUpdateStarted,
|
|
EmailSent: true,
|
|
EmailSentOn: time.Now(),
|
|
}, nil
|
|
}
|
|
us.ExpectedError = user.ErrUserNotFound
|
|
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.ErrorIs(t, err, user.ErrUserNotFound)
|
|
})
|
|
|
|
t.Run("should update user email on valid code", func(t *testing.T) {
|
|
var c calls
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return &tempuser.TempUserDTO{
|
|
Status: tempuser.TmpUserEmailUpdateStarted,
|
|
Name: string(user.EmailUpdateAction),
|
|
InvitedByID: 1,
|
|
Email: "updated@email.com",
|
|
EmailSent: true,
|
|
EmailSentOn: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
ts.UpdateTempUserStatusFN = func(ctx context.Context, cmd *tempuser.UpdateTempUserStatusCommand) error {
|
|
c.updateStatusCalled = true
|
|
return nil
|
|
}
|
|
|
|
is.RemoveIDTokenFn = func(ctx context.Context, identity identity.Requester) error {
|
|
c.removeTokenCalled = true
|
|
return nil
|
|
}
|
|
|
|
us.ExpectedUser = &user.User{Email: "initial@email.com"}
|
|
us.ExpectedError = nil
|
|
us.UpdateFn = func(ctx context.Context, cmd *user.UpdateUserCommand) error {
|
|
c.updateCalled = true
|
|
assert.True(t, *cmd.EmailVerified)
|
|
assert.Equal(t, int64(1), cmd.UserID)
|
|
assert.Equal(t, "", cmd.Login)
|
|
assert.Equal(t, "updated@email.com", cmd.Email)
|
|
return nil
|
|
}
|
|
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.NoError(t, err)
|
|
assert.True(t, c.updateCalled)
|
|
assert.True(t, c.updateStatusCalled)
|
|
assert.True(t, c.removeTokenCalled)
|
|
})
|
|
|
|
t.Run("should update user email and login if login is an email on valid code", func(t *testing.T) {
|
|
var c calls
|
|
ts.GetTempUserByCodeFN = func(ctx context.Context, query *tempuser.GetTempUserByCodeQuery) (*tempuser.TempUserDTO, error) {
|
|
return &tempuser.TempUserDTO{
|
|
Status: tempuser.TmpUserEmailUpdateStarted,
|
|
Name: string(user.EmailUpdateAction),
|
|
InvitedByID: 1,
|
|
Email: "updated@email.com",
|
|
EmailSent: true,
|
|
EmailSentOn: time.Now(),
|
|
}, nil
|
|
}
|
|
|
|
ts.UpdateTempUserStatusFN = func(ctx context.Context, cmd *tempuser.UpdateTempUserStatusCommand) error {
|
|
c.updateStatusCalled = true
|
|
return nil
|
|
}
|
|
|
|
is.RemoveIDTokenFn = func(ctx context.Context, identity identity.Requester) error {
|
|
c.removeTokenCalled = true
|
|
return nil
|
|
}
|
|
|
|
us.ExpectedUser = &user.User{Email: "initial@email.com", Login: "other@email.com"}
|
|
us.ExpectedError = nil
|
|
us.UpdateFn = func(ctx context.Context, cmd *user.UpdateUserCommand) error {
|
|
c.updateCalled = true
|
|
assert.True(t, *cmd.EmailVerified)
|
|
assert.Equal(t, int64(1), cmd.UserID)
|
|
assert.Equal(t, "updated@email.com", cmd.Email)
|
|
assert.Equal(t, "updated@email.com", cmd.Login)
|
|
return nil
|
|
}
|
|
|
|
err := verifier.Complete(context.Background(), user.CompleteEmailVerifyCommand{Code: "some-code"})
|
|
assert.NoError(t, err)
|
|
assert.True(t, c.updateCalled)
|
|
assert.True(t, c.updateStatusCalled)
|
|
assert.True(t, c.removeTokenCalled)
|
|
})
|
|
}
|