diff --git a/pkg/cmd/grafana-cli/commands/commands.go b/pkg/cmd/grafana-cli/commands/commands.go index a4edda3aabe..046c0b8b17e 100644 --- a/pkg/cmd/grafana-cli/commands/commands.go +++ b/pkg/cmd/grafana-cli/commands/commands.go @@ -160,6 +160,11 @@ var adminCommands = []*cli.Command{ Usage: "Read the password from stdin", Value: false, }, + &cli.IntFlag{ + Name: "user-id", + Usage: "The admin user's ID", + Value: DefaultAdminUserId, + }, }, }, { diff --git a/pkg/cmd/grafana-cli/commands/reset_password_command.go b/pkg/cmd/grafana-cli/commands/reset_password_command.go index 583a6934c87..993e0d61387 100644 --- a/pkg/cmd/grafana-cli/commands/reset_password_command.go +++ b/pkg/cmd/grafana-cli/commands/reset_password_command.go @@ -7,6 +7,7 @@ import ( "os" "github.com/fatih/color" + "github.com/grafana/grafana/pkg/cmd/grafana-cli/logger" "github.com/grafana/grafana/pkg/cmd/grafana-cli/runner" "github.com/grafana/grafana/pkg/cmd/grafana-cli/utils" @@ -15,10 +16,11 @@ import ( "github.com/grafana/grafana/pkg/util" ) -const AdminUserId = 1 +const DefaultAdminUserId = 1 func resetPasswordCommand(c utils.CommandLine, runner runner.Runner) error { newPassword := "" + adminId := int64(c.Int("user-id")) if c.Bool("password-from-stdin") { logger.Infof("New Password: ") @@ -35,17 +37,28 @@ func resetPasswordCommand(c utils.CommandLine, runner runner.Runner) error { newPassword = c.Args().First() } + err := resetPassword(adminId, newPassword, runner.UserService) + if err == nil { + logger.Infof("\n") + logger.Infof("Admin password changed successfully %s", color.GreenString("✔")) + } + return err +} + +func resetPassword(adminId int64, newPassword string, userSvc user.Service) error { password := models.Password(newPassword) if password.IsWeak() { return fmt.Errorf("new password is too short") } - userQuery := user.GetUserByIDQuery{ID: AdminUserId} - - usr, err := runner.UserService.GetByID(context.Background(), &userQuery) + userQuery := user.GetUserByIDQuery{ID: adminId} + usr, err := userSvc.GetByID(context.Background(), &userQuery) if err != nil { return fmt.Errorf("could not read user from database. Error: %v", err) } + if !usr.IsAdmin { + return ErrMustBeAdmin + } passwordHashed, err := util.EncodePassword(newPassword, usr.Salt) if err != nil { @@ -53,16 +66,15 @@ func resetPasswordCommand(c utils.CommandLine, runner runner.Runner) error { } cmd := user.ChangeUserPasswordCommand{ - UserID: AdminUserId, + UserID: adminId, NewPassword: passwordHashed, } - if err := runner.UserService.ChangePassword(context.Background(), &cmd); err != nil { + if err := userSvc.ChangePassword(context.Background(), &cmd); err != nil { return fmt.Errorf("failed to update user password: %w", err) } - logger.Infof("\n") - logger.Infof("Admin password changed successfully %s", color.GreenString("✔")) - return nil } + +var ErrMustBeAdmin = fmt.Errorf("reset-admin-password can only be used to reset an admin user account") diff --git a/pkg/cmd/grafana-cli/commands/reset_password_command_test.go b/pkg/cmd/grafana-cli/commands/reset_password_command_test.go new file mode 100644 index 00000000000..7cd16cd192c --- /dev/null +++ b/pkg/cmd/grafana-cli/commands/reset_password_command_test.go @@ -0,0 +1,54 @@ +package commands + +import ( + "testing" + + "github.com/stretchr/testify/require" + + "github.com/grafana/grafana/pkg/services/user" + "github.com/grafana/grafana/pkg/services/user/usertest" +) + +func TestResetPassword(t *testing.T) { + tests := map[string]struct { + UserID int64 + IsAdmin bool + ExpectErr error + }{ + "basic success": { + DefaultAdminUserId, + true, + nil, + }, + "default user is not an admin": { + DefaultAdminUserId, + false, + ErrMustBeAdmin, + }, + "random user is not an admin": { + 11, + false, + ErrMustBeAdmin, + }, + "random user is an admin": { + 11, + true, + nil, + }, + } + + for name, test := range tests { + t.Run(name, func(t *testing.T) { + svc := &usertest.FakeUserService{} + svc.ExpectedUser = &user.User{ + IsAdmin: test.IsAdmin, + } + err := resetPassword(test.UserID, "s00pers3cure!", svc) + if test.ExpectErr != nil { + require.EqualError(t, err, test.ExpectErr.Error()) + } else { + require.NoError(t, err) + } + }) + } +}