user.go

  1package cmd
  2
  3import (
  4	"sort"
  5	"strings"
  6
  7	"github.com/charmbracelet/soft-serve/pkg/backend"
  8	"github.com/charmbracelet/soft-serve/pkg/proto"
  9	"github.com/charmbracelet/soft-serve/pkg/sshutils"
 10	"github.com/spf13/cobra"
 11	"golang.org/x/crypto/ssh"
 12)
 13
 14// UserCommand returns the user subcommand.
 15func UserCommand() *cobra.Command {
 16	cmd := &cobra.Command{
 17		Use:     "user",
 18		Aliases: []string{"users"},
 19		Short:   "Manage users",
 20	}
 21
 22	var admin bool
 23	var key string
 24	userCreateCommand := &cobra.Command{
 25		Use:               "create USERNAME",
 26		Short:             "Create a new user",
 27		Args:              cobra.ExactArgs(1),
 28		PersistentPreRunE: checkIfAdmin,
 29		RunE: func(cmd *cobra.Command, args []string) error {
 30			var pubkeys []ssh.PublicKey
 31			ctx := cmd.Context()
 32			be := backend.FromContext(ctx)
 33			username := args[0]
 34			if key != "" {
 35				pk, _, err := sshutils.ParseAuthorizedKey(key)
 36				if err != nil {
 37					return err //nolint:wrapcheck
 38				}
 39
 40				pubkeys = []ssh.PublicKey{pk}
 41			}
 42
 43			opts := proto.UserOptions{
 44				Admin:      admin,
 45				PublicKeys: pubkeys,
 46			}
 47
 48			_, err := be.CreateUser(ctx, username, opts)
 49			return err //nolint:wrapcheck
 50		},
 51	}
 52
 53	userCreateCommand.Flags().BoolVarP(&admin, "admin", "a", false, "make the user an admin")
 54	userCreateCommand.Flags().StringVarP(&key, "key", "k", "", "add a public key to the user")
 55
 56	userDeleteCommand := &cobra.Command{
 57		Use:               "delete USERNAME",
 58		Short:             "Delete a user",
 59		Args:              cobra.ExactArgs(1),
 60		PersistentPreRunE: checkIfAdmin,
 61		RunE: func(cmd *cobra.Command, args []string) error {
 62			ctx := cmd.Context()
 63			be := backend.FromContext(ctx)
 64			username := args[0]
 65
 66			return be.DeleteUser(ctx, username)
 67		},
 68	}
 69
 70	userListCommand := &cobra.Command{
 71		Use:               "list",
 72		Aliases:           []string{"ls"},
 73		Short:             "List users",
 74		Args:              cobra.NoArgs,
 75		PersistentPreRunE: checkIfAdmin,
 76		RunE: func(cmd *cobra.Command, _ []string) error {
 77			ctx := cmd.Context()
 78			be := backend.FromContext(ctx)
 79			users, err := be.Users(ctx)
 80			if err != nil {
 81				return err //nolint:wrapcheck
 82			}
 83
 84			sort.Strings(users)
 85			for _, u := range users {
 86				cmd.Println(u)
 87			}
 88
 89			return nil
 90		},
 91	}
 92
 93	userAddPubkeyCommand := &cobra.Command{
 94		Use:               "add-pubkey USERNAME AUTHORIZED_KEY",
 95		Short:             "Add a public key to a user",
 96		Args:              cobra.MinimumNArgs(2),
 97		PersistentPreRunE: checkIfAdmin,
 98		RunE: func(cmd *cobra.Command, args []string) error {
 99			ctx := cmd.Context()
100			be := backend.FromContext(ctx)
101			username := args[0]
102			pubkey := strings.Join(args[1:], " ")
103			pk, _, err := sshutils.ParseAuthorizedKey(pubkey)
104			if err != nil {
105				return err //nolint:wrapcheck
106			}
107
108			return be.AddPublicKey(ctx, username, pk)
109		},
110	}
111
112	userRemovePubkeyCommand := &cobra.Command{
113		Use:               "remove-pubkey USERNAME AUTHORIZED_KEY",
114		Short:             "Remove a public key from a user",
115		Args:              cobra.MinimumNArgs(2),
116		PersistentPreRunE: checkIfAdmin,
117		RunE: func(cmd *cobra.Command, args []string) error {
118			ctx := cmd.Context()
119			be := backend.FromContext(ctx)
120			username := args[0]
121			pubkey := strings.Join(args[1:], " ")
122			pk, _, err := sshutils.ParseAuthorizedKey(pubkey)
123			if err != nil {
124				return err //nolint:wrapcheck
125			}
126
127			return be.RemovePublicKey(ctx, username, pk)
128		},
129	}
130
131	userSetAdminCommand := &cobra.Command{
132		Use:               "set-admin USERNAME [true|false]",
133		Short:             "Make a user an admin",
134		Args:              cobra.ExactArgs(2),
135		PersistentPreRunE: checkIfAdmin,
136		RunE: func(cmd *cobra.Command, args []string) error {
137			ctx := cmd.Context()
138			be := backend.FromContext(ctx)
139			username := args[0]
140
141			return be.SetAdmin(ctx, username, args[1] == "true")
142		},
143	}
144
145	userInfoCommand := &cobra.Command{
146		Use:               "info USERNAME",
147		Short:             "Show information about a user",
148		Args:              cobra.ExactArgs(1),
149		PersistentPreRunE: checkIfAdmin,
150		RunE: func(cmd *cobra.Command, args []string) error {
151			ctx := cmd.Context()
152			be := backend.FromContext(ctx)
153			username := args[0]
154
155			user, err := be.User(ctx, username)
156			if err != nil {
157				return err //nolint:wrapcheck
158			}
159
160			isAdmin := user.IsAdmin()
161
162			cmd.Printf("Username: %s\n", user.Username())
163			cmd.Printf("Admin: %t\n", isAdmin)
164			cmd.Printf("Public keys:\n")
165			for _, pk := range user.PublicKeys() {
166				cmd.Printf("  %s\n", sshutils.MarshalAuthorizedKey(pk))
167			}
168
169			return nil
170		},
171	}
172
173	userSetUsernameCommand := &cobra.Command{
174		Use:               "set-username USERNAME NEW_USERNAME",
175		Short:             "Change a user's username",
176		Args:              cobra.ExactArgs(2),
177		PersistentPreRunE: checkIfAdmin,
178		RunE: func(cmd *cobra.Command, args []string) error {
179			ctx := cmd.Context()
180			be := backend.FromContext(ctx)
181			username := args[0]
182			newUsername := args[1]
183
184			return be.SetUsername(ctx, username, newUsername)
185		},
186	}
187
188	cmd.AddCommand(
189		userCreateCommand,
190		userAddPubkeyCommand,
191		userInfoCommand,
192		userListCommand,
193		userDeleteCommand,
194		userRemovePubkeyCommand,
195		userSetAdminCommand,
196		userSetUsernameCommand,
197	)
198
199	return cmd
200}