package auth import ( "context" "fmt" "io" "os" "path/filepath" "github.com/influxdata/influx-cli/v2/pkg/tabwriter" "github.com/influxdata/influxdb/v2" "github.com/influxdata/influxdb/v2/authorization" "github.com/influxdata/influxdb/v2/bolt" "github.com/influxdata/influxdb/v2/logger" "github.com/influxdata/influxdb/v2/tenant" "github.com/spf13/cobra" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) func NewAuthCommand() *cobra.Command { base := &cobra.Command{ Use: "auth", Short: "On-disk authorization management commands, for recovery", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { cmd.PrintErrf("See '%s -h' for help\n", cmd.CommandPath()) }, } base.AddCommand(NewAuthListCommand()) base.AddCommand(NewAuthCreateCommand()) return base } type authListCommand struct { logger *zap.Logger boltPath string out io.Writer } func NewAuthListCommand() *cobra.Command { var authCmd authListCommand cmd := &cobra.Command{ Use: "list", Short: "List authorizations", RunE: func(cmd *cobra.Command, args []string) error { config := logger.NewConfig() config.Level = zapcore.InfoLevel newLogger, err := config.New(cmd.ErrOrStderr()) if err != nil { return err } authCmd.logger = newLogger authCmd.out = cmd.OutOrStdout() return authCmd.run() }, } defaultPath := filepath.Join(os.Getenv("HOME"), ".influxdbv2", "influxd.bolt") cmd.Flags().StringVar(&authCmd.boltPath, "bolt-path", defaultPath, "Path to the BoltDB file.") return cmd } func (cmd *authListCommand) run() error { ctx := context.Background() store := bolt.NewKVStore(cmd.logger.With(zap.String("system", "bolt-kvstore")), cmd.boltPath) if err := store.Open(ctx); err != nil { return err } defer store.Close() tenantStore := tenant.NewStore(store) tenantService := tenant.NewService(tenantStore) authStore, err := authorization.NewStore(store) if err != nil { return err } auth := authorization.NewService(authStore, tenantService) filter := influxdb.AuthorizationFilter{} auths, _, err := auth.FindAuthorizations(ctx, filter) if err != nil { return err } return PrintAuth(ctx, cmd.out, auths, tenantService) } type authCreateCommand struct { logger *zap.Logger boltPath string out io.Writer username string org string } func NewAuthCreateCommand() *cobra.Command { var authCmd authCreateCommand cmd := &cobra.Command{ Use: "create-operator", Short: "Create new operator token for a user", RunE: func(cmd *cobra.Command, args []string) error { config := logger.NewConfig() config.Level = zapcore.InfoLevel newLogger, err := config.New(cmd.ErrOrStderr()) if err != nil { return err } authCmd.logger = newLogger authCmd.out = cmd.OutOrStdout() return authCmd.run() }, } defaultPath := filepath.Join(os.Getenv("HOME"), ".influxdbv2", "influxd.bolt") cmd.Flags().StringVar(&authCmd.boltPath, "bolt-path", defaultPath, "Path to the BoltDB file") cmd.Flags().StringVar(&authCmd.username, "username", "", "Name of the user") cmd.Flags().StringVar(&authCmd.org, "org", "", "Name of the org") return cmd } func (cmd *authCreateCommand) run() error { ctx := context.Background() store := bolt.NewKVStore(cmd.logger.With(zap.String("system", "bolt-kvstore")), cmd.boltPath) if err := store.Open(ctx); err != nil { return err } defer store.Close() tenantStore := tenant.NewStore(store) tenantService := tenant.NewService(tenantStore) authStore, err := authorization.NewStore(store) if err != nil { return err } auth := authorization.NewService(authStore, tenantService) if cmd.username == "" { return fmt.Errorf("must provide --username") } if cmd.org == "" { return fmt.Errorf("must provide --org") } // Find the user user, err := tenantService.FindUser(ctx, influxdb.UserFilter{Name: &cmd.username}) if err != nil { return fmt.Errorf("could not find user %q: %w", cmd.username, err) } orgs, _, err := tenantService.FindOrganizations(ctx, influxdb.OrganizationFilter{ Name: &cmd.org, }) if err != nil { return fmt.Errorf("could not find org %q: %w", cmd.org, err) } org := orgs[0] // Create operator token authToCreate := &influxdb.Authorization{ Description: fmt.Sprintf("%s's Recovery Token", cmd.username), Permissions: influxdb.OperPermissions(), UserID: user.ID, OrgID: org.ID, } if err := auth.CreateAuthorization(ctx, authToCreate); err != nil { return fmt.Errorf("could not create recovery token: %w", err) } // Print all authorizations now that we have added one filter := influxdb.AuthorizationFilter{} auths, _, err := auth.FindAuthorizations(ctx, filter) if err != nil { return err } return PrintAuth(ctx, cmd.out, auths, tenantService) } func PrintAuth(ctx context.Context, w io.Writer, v []*influxdb.Authorization, userSvc influxdb.UserService) error { headers := []string{ "ID", "User Name", "User ID", "Description", "Token", "Permissions", } var rows []map[string]interface{} for _, t := range v { user, err := userSvc.FindUserByID(ctx, t.UserID) userName := "" if err == nil && user != nil { userName = user.Name } row := map[string]interface{}{ "ID": t.ID, "Description": t.Description, "User Name": userName, "User ID": t.UserID, "Token": t.Token, "Permissions": t.Permissions, } rows = append(rows, row) } writer := tabwriter.NewTabWriter(w, false) defer writer.Flush() if err := writer.WriteHeaders(headers...); err != nil { return err } for _, row := range rows { if err := writer.Write(row); err != nil { return err } } return nil }