From 54fdff245d4050f7c107ded46d743675f6f91d92 Mon Sep 17 00:00:00 2001 From: Zach Wasserman Date: Mon, 7 Jun 2021 13:23:15 -0700 Subject: [PATCH] Update fleetctl user create for Teams (#995) Can now assign global role and team roles via `fleetctl user create`. --- cmd/fleetctl/user.go | 63 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 53 insertions(+), 10 deletions(-) diff --git a/cmd/fleetctl/user.go b/cmd/fleetctl/user.go index 79706f09fc..8199c28e82 100644 --- a/cmd/fleetctl/user.go +++ b/cmd/fleetctl/user.go @@ -4,19 +4,24 @@ import ( "bytes" "fmt" "os" + "strconv" + "strings" "github.com/fleetdm/fleet/server/fleet" + "github.com/fleetdm/fleet/server/ptr" "github.com/pkg/errors" "github.com/urfave/cli/v2" "golang.org/x/crypto/ssh/terminal" ) const ( - adminFlagName = "admin" - usernameFlagName = "username" - passwordFlagName = "password" - emailFlagName = "email" - ssoFlagName = "sso" + adminFlagName = "admin" + globalRoleFlagName = "global-role" + teamFlagName = "team" + usernameFlagName = "username" + passwordFlagName = "password" + emailFlagName = "email" + ssoFlagName = "sso" ) func userCommand() *cli.Command { @@ -33,7 +38,7 @@ func createUserCommand() *cli.Command { return &cli.Command{ Name: "create", Usage: "Create a new user", - UsageText: `This command will create a new user in Fleet. By default, the user will authenticate with a password and will not have admin privileges. + UsageText: `This command will create a new user in Fleet. By default, the user will authenticate with a password and will be a global observer. If a password is required and not provided by flag, the command will prompt for password input through stdin.`, Flags: []cli.Flag{ @@ -51,14 +56,19 @@ func createUserCommand() *cli.Command { Name: passwordFlagName, Usage: "Password for new user", }, - &cli.BoolFlag{ - Name: adminFlagName, - Usage: "Grant admin privileges to created user (default false)", - }, &cli.BoolFlag{ Name: ssoFlagName, Usage: "Enable user login via SSO (default false)", }, + &cli.StringFlag{ + Name: globalRoleFlagName, + Usage: "Global role to assign to user (default observer)", + }, + &cli.StringSliceFlag{ + Name: "team", + Aliases: []string{"t"}, + Usage: "Team assignments in team_id:role pairs (multiple may be specified)", + }, configFlag(), contextFlag(), yamlFlag(), @@ -74,6 +84,37 @@ func createUserCommand() *cli.Command { password := c.String(passwordFlagName) email := c.String(emailFlagName) sso := c.Bool(ssoFlagName) + globalRoleString := c.String(globalRoleFlagName) + teamStrings := c.StringSlice(teamFlagName) + + var globalRole *string + var teams []fleet.UserTeam + if globalRoleString != "" && len(teamStrings) > 0 { + return errors.New("Users may not have global_role and teams.") + } else if globalRoleString == "" && len(teamStrings) == 0 { + globalRole = ptr.String(fleet.RoleObserver) + } else if globalRoleString != "" { + if !fleet.ValidGlobalRole(globalRoleString) { + return errors.Errorf("'%s' is not a valid team role", globalRoleString) + } + globalRole = ptr.String(globalRoleString) + } else { + for _, t := range teamStrings { + parts := strings.Split(t, ":") + if len(parts) != 2 { + return errors.Errorf("Unable to parse '%s' as team_id:role", t) + } + teamID, err := strconv.Atoi(parts[0]) + if err != nil { + return errors.Wrap(err, "Unable to parse team_id") + } + if !fleet.ValidTeamRole(parts[1]) { + return errors.Errorf("'%s' is not a valid team role", parts[1]) + } + + teams = append(teams, fleet.UserTeam{Team: fleet.Team{ID: uint(teamID)}, Role: parts[1]}) + } + } if sso && len(password) > 0 { return fmt.Errorf("Password may not be provided for SSO users.") @@ -112,6 +153,8 @@ func createUserCommand() *cli.Command { Email: &email, SSOEnabled: &sso, AdminForcedPasswordReset: &force_reset, + GlobalRole: globalRole, + Teams: &teams, }) if err != nil { return errors.Wrap(err, "Failed to create user")