forked from gravitational/teleport
-
Notifications
You must be signed in to change notification settings - Fork 0
/
user_command.go
180 lines (159 loc) · 5.77 KB
/
user_command.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
/*
Copyright 2015-2017 Gravitational, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package common
import (
"fmt"
"strings"
"time"
"github.com/gravitational/kingpin"
"github.com/gravitational/teleport"
"github.com/gravitational/teleport/lib/asciitable"
"github.com/gravitational/teleport/lib/auth"
"github.com/gravitational/teleport/lib/defaults"
"github.com/gravitational/teleport/lib/service"
"github.com/gravitational/teleport/lib/services"
"github.com/gravitational/teleport/lib/web"
"github.com/gravitational/trace"
)
// UserCommand implements `tctl users` set of commands
// It implements CLICommand interface
type UserCommand struct {
config *service.Config
login string
allowedLogins string
kubeGroups string
roles string
identities []string
ttl time.Duration
userAdd *kingpin.CmdClause
userUpdate *kingpin.CmdClause
userList *kingpin.CmdClause
userDelete *kingpin.CmdClause
}
// Initialize allows UserCommand to plug itself into the CLI parser
func (u *UserCommand) Initialize(app *kingpin.Application, config *service.Config) {
u.config = config
users := app.Command("users", "Manage user accounts")
u.userAdd = users.Command("add", "Generate a user invitation token")
u.userAdd.Arg("account", "Teleport user account name").Required().StringVar(&u.login)
u.userAdd.Arg("local-logins", "Local UNIX users this account can log in as [login]").
Default("").StringVar(&u.allowedLogins)
u.userAdd.Flag("k8s-groups", "Kubernetes groups to assign to a user.").
Default("").StringVar(&u.kubeGroups)
u.userAdd.Flag("ttl", fmt.Sprintf("Set expiration time for token, default is %v hour, maximum is %v hours",
int(defaults.SignupTokenTTL/time.Hour), int(defaults.MaxSignupTokenTTL/time.Hour))).
Default(fmt.Sprintf("%v", defaults.SignupTokenTTL)).DurationVar(&u.ttl)
u.userAdd.Alias(AddUserHelp)
u.userUpdate = users.Command("update", "Update properties for existing user").Hidden()
u.userUpdate.Arg("login", "Teleport user login").Required().StringVar(&u.login)
u.userUpdate.Flag("set-roles", "Roles to assign to this user").
Default("").StringVar(&u.roles)
u.userList = users.Command("ls", "List all user accounts")
u.userDelete = users.Command("rm", "Deletes user accounts").Alias("del")
u.userDelete.Arg("logins", "Comma-separated list of user logins to delete").
Required().StringVar(&u.login)
}
// TryRun takes the CLI command as an argument (like "users add") and executes it.
func (u *UserCommand) TryRun(cmd string, client auth.ClientI) (match bool, err error) {
switch cmd {
case u.userAdd.FullCommand():
err = u.Add(client)
case u.userUpdate.FullCommand():
err = u.Update(client)
case u.userList.FullCommand():
err = u.List(client)
case u.userDelete.FullCommand():
err = u.Delete(client)
default:
return false, nil
}
return true, trace.Wrap(err)
}
// Add creates a new sign-up token and prints a token URL to stdout.
// A user is not created until he visits the sign-up URL and completes the process
func (u *UserCommand) Add(client auth.ClientI) error {
// if no local logins were specified, default to 'login'
if u.allowedLogins == "" {
u.allowedLogins = u.login
}
var kubeGroups []string
if u.kubeGroups != "" {
kubeGroups = strings.Split(u.kubeGroups, ",")
}
user := services.UserV1{
Name: u.login,
AllowedLogins: strings.Split(u.allowedLogins, ","),
KubeGroups: kubeGroups,
}
token, err := client.CreateSignupToken(user, u.ttl)
if err != nil {
return err
}
// try to auto-suggest the activation link
u.PrintSignupURL(client, token, u.ttl)
return nil
}
func (u *UserCommand) PrintSignupURL(client auth.ClientI, token string, ttl time.Duration) {
signupURL, proxyHost := web.CreateSignupLink(client, token)
fmt.Printf("Signup token has been created and is valid for %v hours. Share this URL with the user:\n%v\n\n",
int(ttl/time.Hour), signupURL)
fmt.Printf("NOTE: Make sure %v points at a Teleport proxy which users can access.\n", proxyHost)
}
// Update updates existing user
func (u *UserCommand) Update(client auth.ClientI) error {
user, err := client.GetUser(u.login)
if err != nil {
return trace.Wrap(err)
}
roles := strings.Split(u.roles, ",")
for _, role := range roles {
if _, err := client.GetRole(role); err != nil {
return trace.Wrap(err)
}
}
user.SetRoles(roles)
if err := client.UpsertUser(user); err != nil {
return trace.Wrap(err)
}
fmt.Printf("%v has been updated with roles %v\n", user.GetName(), strings.Join(user.GetRoles(), ","))
return nil
}
// List prints all existing user accounts
func (u *UserCommand) List(client auth.ClientI) error {
users, err := client.GetUsers()
if err != nil {
return trace.Wrap(err)
}
if len(users) == 0 {
fmt.Println("No users found")
return nil
}
t := asciitable.MakeTable([]string{"User", "Allowed logins"})
for _, u := range users {
logins, _ := u.GetTraits()[teleport.TraitLogins]
t.AddRow([]string{u.GetName(), strings.Join(logins, ",")})
}
fmt.Println(t.AsBuffer().String())
return nil
}
// Delete deletes teleport user(s). User IDs are passed as a comma-separated
// list in UserCommand.login
func (u *UserCommand) Delete(client auth.ClientI) error {
for _, l := range strings.Split(u.login, ",") {
if err := client.DeleteUser(l); err != nil {
return trace.Wrap(err)
}
fmt.Printf("User '%v' has been deleted\n", l)
}
return nil
}