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