1package cmd23import (4 "sort"5 "strings"67 "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)1314// 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 }2122 var admin bool23 var key string24 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.PublicKey31 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 err38 }3940 pubkeys = []ssh.PublicKey{pk}41 }4243 opts := proto.UserOptions{44 Admin: admin,45 PublicKeys: pubkeys,46 }4748 _, err := be.CreateUser(ctx, username, opts)49 return err50 },51 }5253 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")5556 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]6566 return be.DeleteUser(ctx, username)67 },68 }6970 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 err82 }8384 sort.Strings(users)85 for _, u := range users {86 cmd.Println(u)87 }8889 return nil90 },91 }9293 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 err106 }107108 return be.AddPublicKey(ctx, username, pk)109 },110 }111112 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 err125 }126127 return be.RemovePublicKey(ctx, username, pk)128 },129 }130131 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]140141 return be.SetAdmin(ctx, username, args[1] == "true")142 },143 }144145 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]154155 user, err := be.User(ctx, username)156 if err != nil {157 return err158 }159160 isAdmin := user.IsAdmin()161162 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 }168169 return nil170 },171 }172173 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]183184 return be.SetUsername(ctx, username, newUsername)185 },186 }187188 cmd.AddCommand(189 userCreateCommand,190 userAddPubkeyCommand,191 userInfoCommand,192 userListCommand,193 userDeleteCommand,194 userRemovePubkeyCommand,195 userSetAdminCommand,196 userSetUsernameCommand,197 )198199 return cmd200}