-
Notifications
You must be signed in to change notification settings - Fork 552
/
user.go
205 lines (174 loc) · 6.17 KB
/
user.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// Copyright 2021 Vectorized, Inc.
//
// Use of this software is governed by the Business Source License
// included in the file licenses/BSL.md
//
// As of the Change Date specified in that file, in accordance with
// the Business Source License, use of this software will be governed
// by the Apache License, Version 2.0
package acl
import (
"fmt"
"strings"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/vectorizedio/redpanda/src/go/rpk/pkg/api/admin"
"github.com/vectorizedio/redpanda/src/go/rpk/pkg/config"
"github.com/vectorizedio/redpanda/src/go/rpk/pkg/out"
)
func NewUserCommand(fs afero.Fs) *cobra.Command {
var apiUrls []string
cmd := &cobra.Command{
Use: "user",
Short: "Manage SASL users.",
Long: `Manage SASL users.
If SASL is enabled, a SASL user is what you use to talk to Redpanda, and ACLs
control what your user has access to. See 'rpk acl --help' for more information
about ACLs, and 'rpk acl user create --help' for more information about
creating SASL users. Using SASL requires setting "enable_sasl: true" in the
redpanda section of your redpanda.yaml.
`,
}
cmd.PersistentFlags().StringSliceVar(
&apiUrls,
config.FlagAdminHosts2,
[]string{},
"The comma-separated list of Admin API addresses (<IP>:<port>)."+
" You must specify one for each node.",
)
cmd.AddCommand(NewCreateUserCommand(fs))
cmd.AddCommand(NewDeleteUserCommand(fs))
cmd.AddCommand(NewListUsersCommand(fs))
return cmd
}
// UserAPI encapsulates functions needed for a user API.
type UserAPI interface {
CreateUser(username, password string) error
DeleteUser(username string) error
ListUsers() ([]string, error)
}
func NewCreateUserCommand(fs afero.Fs) *cobra.Command {
var userOld, pass, passOld, mechanism string
cmd := &cobra.Command{
Use: "create [USER} -p [PASS]",
Short: "Create a SASL user.",
Long: `Create a SASL user.
This command creates a single SASL user with the given password, optionally
with a custom "mechanism". SASL consists of three parts: a username, a
password, and a mechanism. The mechanism determines which authentication flow
the client will use for this user/pass.
Redpanda currently supports two mechanisms: SCRAM-SHA-256, the default, and
SCRAM-SHA-512, which is the same flow but uses sha512 rather than sha256.
Using SASL requires setting "enable_sasl: true" in the redpanda section of your
redpanda.yaml. Before a created SASL account can be used, you must also create
ACLs to grant the account access to certain resources in your cluster. See the
acl help text for more info.
`,
Args: cobra.MaximumNArgs(1), // when the deprecated user flag is removed, change this to cobra.ExactArgs(1)
Run: func(cmd *cobra.Command, args []string) {
p := config.ParamsFromCommand(cmd)
cfg, err := p.Load(fs)
out.MaybeDie(err, "unable to load config: %v", err)
cl, err := admin.NewClient(fs, cfg)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
// Backwards compatibility: we favor the new user
// format and the new password flag. If either are
// empty, we check the old. If either of those are
// empty, we error.
var user string
if len(args) > 0 {
user = args[0]
} else if userOld != "" { // backcompat
user = userOld
} else {
out.Die("missing required username argument")
}
if pass == "" {
if passOld == "" { // backcompat
out.Die("missing required --password")
}
pass = passOld
}
switch strings.ToLower(mechanism) {
case "scram-sha-256":
mechanism = admin.ScramSha256
case "scram-sha-512":
mechanism = admin.ScramSha512
default:
out.Die("unsupported mechanism %q", mechanism)
}
err = cl.CreateUser(user, pass, mechanism)
out.MaybeDie(err, "unable to create user %q: %v", user, err)
fmt.Printf("Created user %q.\n", user)
},
}
cmd.Flags().StringVar(&userOld, "new-username", "", "")
cmd.Flags().MarkDeprecated("new-username", "the username now does not require a flag") // Oct 2021
cmd.Flags().StringVarP(&pass, "password", "p", "", "new user's password")
cmd.Flags().StringVar(&passOld, "new-password", "", "")
cmd.Flags().MarkDeprecated("new-password", "renamed to --password") // Oct 2021
cmd.Flags().StringVar(
&mechanism,
"mechanism",
strings.ToLower(admin.ScramSha256),
"SASL mechanism to use (scram-sha-256, scram-sha-512, case insensitive)",
)
return cmd
}
func NewDeleteUserCommand(fs afero.Fs) *cobra.Command {
var oldUser string
cmd := &cobra.Command{
Use: "delete [USER]",
Short: "Delete a SASL user.",
Long: `Delete a SASL user.
This command deletes the specified SASL account from Redpanda. This does not
delete any ACLs that may exist for this user.
`,
Args: cobra.MaximumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
p := config.ParamsFromCommand(cmd)
cfg, err := p.Load(fs)
out.MaybeDie(err, "unable to load config: %v", err)
cl, err := admin.NewClient(fs, cfg)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
// Backwards compat: we favor the new format (an
// argument), but if that is empty, we use the old
// flag. If still empty, we error.
var user string
if len(args) > 0 {
user = args[0]
} else if len(oldUser) > 0 {
user = oldUser
} else {
out.Die("missing required username argument")
}
err = cl.DeleteUser(user)
out.MaybeDie(err, "unable to delete user %q: %s", user, err)
fmt.Printf("Deleted user %q.\n", user)
},
}
cmd.Flags().StringVar(&oldUser, "delete-username", "", "The user to be deleted")
cmd.Flags().MarkDeprecated("delete-username", "the username now does not require a flag")
return cmd
}
func NewListUsersCommand(fs afero.Fs) *cobra.Command {
return &cobra.Command{
Use: "list",
Aliases: []string{"ls"},
Short: "List SASL users.",
Run: func(cmd *cobra.Command, _ []string) {
p := config.ParamsFromCommand(cmd)
cfg, err := p.Load(fs)
out.MaybeDie(err, "unable to load config: %v", err)
cl, err := admin.NewClient(fs, cfg)
out.MaybeDie(err, "unable to initialize admin client: %v", err)
users, err := cl.ListUsers()
out.MaybeDie(err, "unable to list users: %v", err)
tw := out.NewTable("Username")
defer tw.Flush()
for _, u := range users {
tw.Print(u)
}
},
}
}