user.go

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