From a3d688c8ed8525ac812ca652739660b51ee02310 Mon Sep 17 00:00:00 2001 From: Misi Date: Tue, 27 Aug 2024 16:01:33 +0200 Subject: [PATCH] CLI: Provide the list of admins if the admin with the default ID was not found (#92217) * Provide the list of admins if the admin with the default ID was not found * Clean up * Update docs * Update docs/sources/cli.md Co-authored-by: Dan Cech * Update pkg/cmd/grafana-cli/commands/reset_password_command.go Co-authored-by: Dan Cech * Lint --------- Co-authored-by: Dan Cech --- docs/sources/cli.md | 10 +++- .../setup-grafana/installation/mac/index.md | 16 +++++ .../commands/reset_password_command.go | 60 ++++++++++++++++++- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/docs/sources/cli.md b/docs/sources/cli.md index 4dd670751c5..3352c8e85ba 100644 --- a/docs/sources/cli.md +++ b/docs/sources/cli.md @@ -211,7 +211,7 @@ grafana cli admin ### Reset admin password -`grafana cli admin reset-admin-password ` resets the password for the admin user using the CLI. You might need to do this if you lose the admin password. +`grafana cli admin reset-admin-password ` resets the password for the admin user using the CLI. You might need to do this if you lose the admin password. By default, this command uses the default ID of the admin user, which is 1. If you know the ID of the admin user, you can use the `--user-id` flag to specify the user ID. If the `--user-id` flag is not specified and the command cannot find the admin user, it returns the list of current admin users' username and ID. From that list you can determine the ID of the admin user and run the command again with the `--user-id` flag. If there are two flags being used to set the homepath and the config file path, then running the command returns this error: @@ -227,6 +227,14 @@ If you have not lost the admin password, we recommend that you change the user p If you need to set the password in a script, then you can use the [Grafana User API]({{< relref "./developers/http_api/user/#change-password" >}}). +#### Reset admin password + +If you installed Grafana using Homebrew, you can reset the admin password using the following command: + +```bash +/opt/homebrew/opt/grafana/bin/grafana cli --config /opt/homebrew/etc/grafana/grafana.ini --homepath /opt/homebrew/opt/grafana/share/grafana --configOverrides cfg:default.paths.data=/opt/homebrew/var/lib/grafana admin reset-admin-password +``` + ### Migrate data and encrypt passwords `data-migration` runs a script that migrates or cleans up data in your database. diff --git a/docs/sources/setup-grafana/installation/mac/index.md b/docs/sources/setup-grafana/installation/mac/index.md index 3b8c2dd008e..55d447c17ae 100644 --- a/docs/sources/setup-grafana/installation/mac/index.md +++ b/docs/sources/setup-grafana/installation/mac/index.md @@ -45,6 +45,22 @@ To install Grafana on macOS using Homebrew, complete the following steps: brew services start grafana ``` +### Using the Grafana CLI with Homebrew + +To use the Grafana CLI with Homebrew, you need to append the home path, the config file path and - based on the command - some other configurations to the `cli` command: + +For `admin` commands, you need to append the `--configOverrides cfg:default.paths.data=/opt/homebrew/var/lib/grafana` configuration. Example: + +```bash +/opt/homebrew/opt/grafana/bin/grafana cli --config /opt/homebrew/etc/grafana/grafana.ini --homepath /opt/homebrew/opt/grafana/share/grafana --configOverrides cfg:default.paths.data=/opt/homebrew/var/lib/grafana admin reset-admin-password +``` + +For `plugins` commands, you need to append the `--pluginsDir /opt/homebrew/var/lib/grafana/plugins` configuration. Example: + +```bash +/opt/homebrew/opt/grafana/bin/grafana cli --config /opt/homebrew/etc/grafana/grafana.ini --homepath /opt/homebrew/opt/grafana/share/grafana --pluginsDir "/opt/homebrew/var/lib/grafana/plugins" plugins install +``` + ## Install standalone macOS binaries To install Grafana on macOS using the standalone binaries, complete the following steps: diff --git a/pkg/cmd/grafana-cli/commands/reset_password_command.go b/pkg/cmd/grafana-cli/commands/reset_password_command.go index 0a6cfbaae06..316372f6766 100644 --- a/pkg/cmd/grafana-cli/commands/reset_password_command.go +++ b/pkg/cmd/grafana-cli/commands/reset_password_command.go @@ -3,6 +3,7 @@ package commands import ( "bufio" "context" + "errors" "fmt" "os" @@ -16,6 +17,11 @@ import ( const DefaultAdminUserId = 1 +var ( + ErrMustBeAdmin = fmt.Errorf("reset-admin-password can only be used to reset an admin user account") + ErrAdminCannotBeFound = errors.New("admin user cannot be found") +) + func resetPasswordCommand(c utils.CommandLine, runner server.Runner) error { var newPassword user.Password adminId := int64(c.Int("user-id")) @@ -44,15 +50,35 @@ func resetPasswordCommand(c utils.CommandLine, runner server.Runner) error { logger.Infof("\n") logger.Infof("Admin password changed successfully %s", color.GreenString("✔")) } - return err + + if errors.Is(err, ErrAdminCannotBeFound) { + logger.Infof("\n") + logger.Infof("Admin user cannot be found %s. \n", color.RedString("✘")) + admins, err := listAdminUsers(runner.UserService) + if err != nil { + return fmt.Errorf("failed to list admin users: %w", err) + } + + logger.Infof("\n") + logger.Infof("Please try to run the command again specifying a user-id (--user-id) from the list below:\n") + for _, u := range admins { + logger.Infof("\t Username: %s ID: %d\n", u.Login, u.ID) + } + } + + return nil } func resetPassword(adminId int64, password user.Password, userSvc user.Service) error { userQuery := user.GetUserByIDQuery{ID: adminId} usr, err := userSvc.GetByID(context.Background(), &userQuery) if err != nil { + if errors.Is(err, user.ErrUserNotFound) { + return ErrAdminCannotBeFound + } return fmt.Errorf("could not read user from database. Error: %v", err) } + if !usr.IsAdmin { return ErrMustBeAdmin } @@ -64,4 +90,34 @@ func resetPassword(adminId int64, password user.Password, userSvc user.Service) return nil } -var ErrMustBeAdmin = fmt.Errorf("reset-admin-password can only be used to reset an admin user account") +func listAdminUsers(userSvc user.Service) ([]*user.UserSearchHitDTO, error) { + searchAdminsQuery := user.SearchUsersQuery{ + Filters: []user.Filter{&adminFilter{}}, + SignedInUser: &user.SignedInUser{ + Permissions: map[int64]map[string][]string{0: {"users:read": {"global.users:*"}}}, + }, + } + + admins, err := userSvc.Search(context.Background(), &searchAdminsQuery) + if err != nil { + return nil, fmt.Errorf("could not read user from database. Error: %v", err) + } + + return admins.Users, nil +} + +type adminFilter struct{} + +func (f *adminFilter) WhereCondition() *user.WhereCondition { + return &user.WhereCondition{ + Condition: "is_admin = 1", + } +} + +func (f *adminFilter) JoinCondition() *user.JoinCondition { + return nil +} + +func (f *adminFilter) InCondition() *user.InCondition { + return nil +}