Skip to content

Commit a40db60

Browse files
authored
Support ACL and migrate to v0.5.0 (#19)
* ACL support * use the latest version
1 parent 12043dc commit a40db60

File tree

7 files changed

+2385
-11
lines changed

7 files changed

+2385
-11
lines changed

cmd/root.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/pbufio/pbuf-cli/internal/modules"
1515
"github.com/pbufio/pbuf-cli/internal/registry"
1616
"github.com/spf13/cobra"
17+
"google.golang.org/grpc"
1718
)
1819

1920
// NewRootCmd creates cobra root command via function
@@ -60,16 +61,20 @@ func NewRootCmd() *cobra.Command {
6061
}
6162

6263
if modulesConfig.HasRegistry() {
63-
var registryClient v1.RegistryClient
64+
var conn grpc.ClientConnInterface
6465
if modulesConfig.Registry.Insecure {
65-
registryClient = registry.NewInsecureClient(modulesConfig, netrcAuth)
66+
conn = registry.NewInsecureConn(modulesConfig, netrcAuth)
6667
} else {
67-
registryClient = registry.NewSecureClient(modulesConfig, netrcAuth)
68+
conn = registry.NewSecureConn(modulesConfig, netrcAuth)
6869
}
6970

71+
registryClient := v1.NewRegistryClient(conn)
72+
usersClient := v1.NewUserServiceClient(conn)
73+
7074
rootCmd.AddCommand(NewModuleCmd(modulesConfig, registryClient))
7175
rootCmd.AddCommand(NewVendorCmd(modulesConfig, netrcAuth, registryClient))
7276
rootCmd.AddCommand(NewAuthCmd(modulesConfig, usr, netrcAuth))
77+
rootCmd.AddCommand(NewUsersCmd(modulesConfig, usersClient))
7378
} else {
7479
rootCmd.AddCommand(NewVendorCmd(modulesConfig, netrcAuth, nil))
7580
}

cmd/users.go

Lines changed: 332 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,332 @@
1+
package cmd
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"fmt"
7+
"log"
8+
"strings"
9+
10+
v1 "github.com/pbufio/pbuf-cli/gen/pbuf-registry/v1"
11+
"github.com/pbufio/pbuf-cli/internal/model"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
func NewUsersCmd(_ *model.Config, client v1.UserServiceClient) *cobra.Command {
16+
usersCmd := &cobra.Command{
17+
Use: "users",
18+
Short: "Users",
19+
Long: "Users is a command to manage users, bots, and permissions",
20+
RunE: func(cmd *cobra.Command, args []string) error {
21+
return cmd.Help()
22+
},
23+
}
24+
25+
usersCmd.AddCommand(newCreateUserCmd(client))
26+
usersCmd.AddCommand(newListUsersCmd(client))
27+
usersCmd.AddCommand(newGetUserCmd(client))
28+
usersCmd.AddCommand(newUpdateUserCmd(client))
29+
usersCmd.AddCommand(newDeleteUserCmd(client))
30+
usersCmd.AddCommand(newRegenerateTokenCmd(client))
31+
usersCmd.AddCommand(newGrantPermissionCmd(client))
32+
usersCmd.AddCommand(newRevokePermissionCmd(client))
33+
usersCmd.AddCommand(newListUserPermissionsCmd(client))
34+
35+
return usersCmd
36+
}
37+
38+
func newCreateUserCmd(client v1.UserServiceClient) *cobra.Command {
39+
createCmd := &cobra.Command{
40+
Use: "create [name]",
41+
Short: "Create",
42+
Long: "Create is a command to create a user or bot",
43+
Args: cobra.ExactArgs(1),
44+
RunE: func(cmd *cobra.Command, args []string) error {
45+
name := args[0]
46+
userTypeStr, err := cmd.Flags().GetString("type")
47+
if err != nil {
48+
return err
49+
}
50+
userType, err := parseUserType(userTypeStr)
51+
if err != nil {
52+
return err
53+
}
54+
55+
resp, err := client.CreateUser(cmd.Context(), &v1.CreateUserRequest{
56+
Name: name,
57+
Type: userType,
58+
})
59+
if err != nil {
60+
return err
61+
}
62+
63+
return printJSON(resp)
64+
},
65+
}
66+
67+
createCmd.Flags().String("type", "user", "user type: user|bot")
68+
return createCmd
69+
}
70+
71+
func newListUsersCmd(client v1.UserServiceClient) *cobra.Command {
72+
listCmd := &cobra.Command{
73+
Use: "list",
74+
Short: "List",
75+
Long: "List is a command to list users and bots",
76+
Args: cobra.ExactArgs(0),
77+
RunE: func(cmd *cobra.Command, args []string) error {
78+
pageSize, err := cmd.Flags().GetInt32("page-size")
79+
if err != nil {
80+
return err
81+
}
82+
page, err := cmd.Flags().GetInt32("page")
83+
if err != nil {
84+
return err
85+
}
86+
87+
resp, err := client.ListUsers(cmd.Context(), &v1.ListUsersRequest{
88+
PageSize: pageSize,
89+
Page: page,
90+
})
91+
if err != nil {
92+
return err
93+
}
94+
95+
return printJSON(resp)
96+
},
97+
}
98+
99+
listCmd.Flags().Int32("page-size", 50, "max number of users to return")
100+
listCmd.Flags().Int32("page", 0, "page number (0-indexed)")
101+
return listCmd
102+
}
103+
104+
func newGetUserCmd(client v1.UserServiceClient) *cobra.Command {
105+
getCmd := &cobra.Command{
106+
Use: "get [id]",
107+
Short: "Get",
108+
Long: "Get is a command to get a user or bot by id",
109+
Args: cobra.ExactArgs(1),
110+
RunE: func(cmd *cobra.Command, args []string) error {
111+
id := args[0]
112+
user, err := client.GetUser(cmd.Context(), &v1.GetUserRequest{Id: id})
113+
if err != nil {
114+
return err
115+
}
116+
return printJSON(user)
117+
},
118+
}
119+
120+
return getCmd
121+
}
122+
123+
func newUpdateUserCmd(client v1.UserServiceClient) *cobra.Command {
124+
updateCmd := &cobra.Command{
125+
Use: "update [id]",
126+
Short: "Update",
127+
Long: "Update is a command to update a user or bot",
128+
Args: cobra.ExactArgs(1),
129+
RunE: func(cmd *cobra.Command, args []string) error {
130+
id := args[0]
131+
name, err := cmd.Flags().GetString("name")
132+
if err != nil {
133+
return err
134+
}
135+
active, err := cmd.Flags().GetBool("active")
136+
if err != nil {
137+
return err
138+
}
139+
inactive, err := cmd.Flags().GetBool("inactive")
140+
if err != nil {
141+
return err
142+
}
143+
144+
if active && inactive {
145+
return errors.New("only one of --active or --inactive can be set")
146+
}
147+
if name == "" && !active && !inactive {
148+
return errors.New("nothing to update: specify --name, --active, or --inactive")
149+
}
150+
151+
isActive := false
152+
switch {
153+
case active:
154+
isActive = true
155+
case inactive:
156+
isActive = false
157+
default:
158+
// Server always applies IsActive from request; preserve current value if not explicitly set.
159+
user, err := client.GetUser(cmd.Context(), &v1.GetUserRequest{Id: id})
160+
if err != nil {
161+
return err
162+
}
163+
isActive = user.IsActive
164+
}
165+
166+
updated, err := client.UpdateUser(cmd.Context(), &v1.UpdateUserRequest{
167+
Id: id,
168+
Name: name,
169+
IsActive: isActive,
170+
})
171+
if err != nil {
172+
return err
173+
}
174+
return printJSON(updated)
175+
},
176+
}
177+
178+
updateCmd.Flags().String("name", "", "new name")
179+
updateCmd.Flags().Bool("active", false, "set user active")
180+
updateCmd.Flags().Bool("inactive", false, "set user inactive")
181+
return updateCmd
182+
}
183+
184+
func newDeleteUserCmd(client v1.UserServiceClient) *cobra.Command {
185+
deleteCmd := &cobra.Command{
186+
Use: "delete [id]",
187+
Short: "Delete",
188+
Long: "Delete is a command to delete a user or bot",
189+
Args: cobra.ExactArgs(1),
190+
RunE: func(cmd *cobra.Command, args []string) error {
191+
id := args[0]
192+
resp, err := client.DeleteUser(cmd.Context(), &v1.DeleteUserRequest{Id: id})
193+
if err != nil {
194+
return err
195+
}
196+
return printJSON(resp)
197+
},
198+
}
199+
200+
return deleteCmd
201+
}
202+
203+
func newRegenerateTokenCmd(client v1.UserServiceClient) *cobra.Command {
204+
regenCmd := &cobra.Command{
205+
Use: "regenerate-token [id]",
206+
Short: "Regenerate token",
207+
Long: "Regenerate token is a command to regenerate a user or bot token",
208+
Args: cobra.ExactArgs(1),
209+
RunE: func(cmd *cobra.Command, args []string) error {
210+
id := args[0]
211+
resp, err := client.RegenerateToken(cmd.Context(), &v1.RegenerateTokenRequest{Id: id})
212+
if err != nil {
213+
return err
214+
}
215+
return printJSON(resp)
216+
},
217+
}
218+
219+
return regenCmd
220+
}
221+
222+
func newGrantPermissionCmd(client v1.UserServiceClient) *cobra.Command {
223+
grantCmd := &cobra.Command{
224+
Use: "grant-permission [user_id] [module_name]",
225+
Short: "Grant permission",
226+
Long: "Grant permission is a command to grant permission to a user or bot",
227+
Args: cobra.ExactArgs(2),
228+
RunE: func(cmd *cobra.Command, args []string) error {
229+
userID := args[0]
230+
moduleName := args[1]
231+
permissionStr, err := cmd.Flags().GetString("permission")
232+
if err != nil {
233+
return err
234+
}
235+
permission, err := parsePermission(permissionStr)
236+
if err != nil {
237+
return err
238+
}
239+
240+
resp, err := client.GrantPermission(cmd.Context(), &v1.GrantPermissionRequest{
241+
UserId: userID,
242+
ModuleName: moduleName,
243+
Permission: permission,
244+
})
245+
if err != nil {
246+
return err
247+
}
248+
return printJSON(resp)
249+
},
250+
}
251+
252+
grantCmd.Flags().String("permission", "", "permission: read|write|admin")
253+
_ = grantCmd.MarkFlagRequired("permission")
254+
return grantCmd
255+
}
256+
257+
func newRevokePermissionCmd(client v1.UserServiceClient) *cobra.Command {
258+
revokeCmd := &cobra.Command{
259+
Use: "revoke-permission [user_id] [module_name]",
260+
Short: "Revoke permission",
261+
Long: "Revoke permission is a command to revoke permission from a user or bot",
262+
Args: cobra.ExactArgs(2),
263+
RunE: func(cmd *cobra.Command, args []string) error {
264+
userID := args[0]
265+
moduleName := args[1]
266+
resp, err := client.RevokePermission(cmd.Context(), &v1.RevokePermissionRequest{
267+
UserId: userID,
268+
ModuleName: moduleName,
269+
})
270+
if err != nil {
271+
return err
272+
}
273+
return printJSON(resp)
274+
},
275+
}
276+
277+
return revokeCmd
278+
}
279+
280+
func newListUserPermissionsCmd(client v1.UserServiceClient) *cobra.Command {
281+
listCmd := &cobra.Command{
282+
Use: "list-permissions [user_id]",
283+
Short: "List permissions",
284+
Long: "List permissions is a command to list permissions for a user or bot",
285+
Args: cobra.ExactArgs(1),
286+
RunE: func(cmd *cobra.Command, args []string) error {
287+
userID := args[0]
288+
resp, err := client.ListUserPermissions(cmd.Context(), &v1.ListUserPermissionsRequest{UserId: userID})
289+
if err != nil {
290+
return err
291+
}
292+
return printJSON(resp)
293+
},
294+
}
295+
296+
return listCmd
297+
}
298+
299+
func parseUserType(s string) (v1.UserType, error) {
300+
s = strings.TrimSpace(strings.ToLower(s))
301+
switch s {
302+
case "", "user":
303+
return v1.UserType_USER_TYPE_USER, nil
304+
case "bot":
305+
return v1.UserType_USER_TYPE_BOT, nil
306+
default:
307+
return v1.UserType_USER_TYPE_UNSPECIFIED, fmt.Errorf("unknown user type %q (expected user|bot)", s)
308+
}
309+
}
310+
311+
func parsePermission(s string) (v1.Permission, error) {
312+
s = strings.TrimSpace(strings.ToLower(s))
313+
switch s {
314+
case "read":
315+
return v1.Permission_PERMISSION_READ, nil
316+
case "write":
317+
return v1.Permission_PERMISSION_WRITE, nil
318+
case "admin":
319+
return v1.Permission_PERMISSION_ADMIN, nil
320+
default:
321+
return v1.Permission_PERMISSION_UNSPECIFIED, fmt.Errorf("unknown permission %q (expected read|write|admin)", s)
322+
}
323+
}
324+
325+
func printJSON(v any) error {
326+
marshalled, err := json.MarshalIndent(v, "", " ")
327+
if err != nil {
328+
return err
329+
}
330+
log.Printf("%+v", string(marshalled))
331+
return nil
332+
}

0 commit comments

Comments
 (0)