Skip to content

Commit

Permalink
feat: add support for setting password and org for a new user in the cli
Browse files Browse the repository at this point in the history
one thing to note here is that new endpoint was created. there was no
endpoint for setting an initial password that worked. The existin endpoint
was a bit messy and coupled across multiple routes. Having multiple auth
schemes proved incredibly taxing to write against.
  • Loading branch information
jsteenb2 committed Nov 19, 2019
1 parent 4d5ac08 commit c8e504c
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 91 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
## v2.0.0-alpha.20 [unreleased]

### Feaures
### Features

1. [15805](https://github.com/influxdata/influxdb/pull/15924) Add tls insecure skip verify to influx CLI.
2. [15981](https://github.com/influxdata/influxdb/pull/15981) Extend influx cli user create to allow for organization ID and user passwords to be set on user.

### Bug Fixes

Expand Down
50 changes: 26 additions & 24 deletions cmd/influx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,27 @@ import (
"github.com/spf13/viper"
)

var influxCmd = &cobra.Command{
Use: "influx",
Short: "Influx Client",
Run: func(cmd *cobra.Command, args []string) {
if err := checkSetup(flags.host); err != nil {
fmt.Printf("Note: %v\n", internal.ErrorFmt(err))
}
cmd.Usage()
},
}

const maxTCPConnections = 128

func init() {
influxCmd.AddCommand(
func main() {
influxCmd := influxCmd()
if err := influxCmd.Execute(); err != nil {
os.Exit(1)
}
}

func influxCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "influx",
Short: "Influx Client",
Run: func(cmd *cobra.Command, args []string) {
if err := checkSetup(flags.host); err != nil {
fmt.Printf("Note: %v\n", internal.ErrorFmt(err))
}
cmd.Usage()
},
}
cmd.AddCommand(
authCmd(),
bucketCmd,
deleteCmd,
Expand All @@ -42,40 +48,36 @@ func init() {
replCmd,
setupCmd,
taskCmd,
userCmd,
userCmd(),
writeCmd,
)

viper.SetEnvPrefix("INFLUX")

influxCmd.PersistentFlags().StringVarP(&flags.token, "token", "t", "", "API token to be used throughout client calls")
cmd.PersistentFlags().StringVarP(&flags.token, "token", "t", "", "API token to be used throughout client calls")
viper.BindEnv("TOKEN")
if h := viper.GetString("TOKEN"); h != "" {
flags.token = h
} else if tok, err := getTokenFromDefaultPath(); err == nil {
flags.token = tok
}

influxCmd.PersistentFlags().StringVar(&flags.host, "host", "http://localhost:9999", "HTTP address of Influx")
cmd.PersistentFlags().StringVar(&flags.host, "host", "http://localhost:9999", "HTTP address of Influx")
viper.BindEnv("HOST")
if h := viper.GetString("HOST"); h != "" {
flags.host = h
}

influxCmd.PersistentFlags().BoolVar(&flags.local, "local", false, "Run commands locally against the filesystem")
cmd.PersistentFlags().BoolVar(&flags.local, "local", false, "Run commands locally against the filesystem")

influxCmd.PersistentFlags().BoolVar(&flags.skipVerify, "skip-verify", false, "SkipVerify controls whether a client verifies the server's certificate chain and host name.")
cmd.PersistentFlags().BoolVar(&flags.skipVerify, "skip-verify", false, "SkipVerify controls whether a client verifies the server's certificate chain and host name.")

// Override help on all the commands tree
walk(influxCmd, func(c *cobra.Command) {
walk(cmd, func(c *cobra.Command) {
c.Flags().BoolP("help", "h", false, fmt.Sprintf("Help for the %s command ", c.Name()))
})
}

func main() {
if err := influxCmd.Execute(); err != nil {
os.Exit(1)
}
return cmd
}

// Flags contains all the CLI flag values for influx.
Expand Down
159 changes: 105 additions & 54 deletions cmd/influx/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"context"
"errors"
"os"

platform "github.com/influxdata/influxdb"
Expand All @@ -10,34 +11,41 @@ import (
"github.com/spf13/cobra"
)

var userCmd = &cobra.Command{
Use: "user",
Short: "User management commands",
Run: func(cmd *cobra.Command, args []string) {
cmd.Usage()
},
func userCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "user",
Short: "User management commands",
Run: func(cmd *cobra.Command, args []string) {
cmd.Usage()
},
}
cmd.AddCommand(
userCreateCmd(),
userDeleteCmd(),
userFindCmd(),
userUpdateCmd(),
)

return cmd
}

// UserUpdateFlags are command line args used when updating a user
type UserUpdateFlags struct {
var userUpdateFlags struct {
id string
name string
}

var userUpdateFlags UserUpdateFlags

func init() {
userUpdateCmd := &cobra.Command{
func userUpdateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "update",
Short: "Update user",
RunE: wrapCheckSetup(userUpdateF),
}

userUpdateCmd.Flags().StringVarP(&userUpdateFlags.id, "id", "i", "", "The user ID (required)")
userUpdateCmd.Flags().StringVarP(&userUpdateFlags.name, "name", "n", "", "The user name")
userUpdateCmd.MarkFlagRequired("id")
cmd.Flags().StringVarP(&userUpdateFlags.id, "id", "i", "", "The user ID (required)")
cmd.Flags().StringVarP(&userUpdateFlags.name, "name", "n", "", "The user name")
cmd.MarkFlagRequired("id")

userCmd.AddCommand(userUpdateCmd)
return cmd
}

func newUserService() (platform.UserService, error) {
Expand Down Expand Up @@ -97,24 +105,25 @@ func userUpdateF(cmd *cobra.Command, args []string) error {
return nil
}

// UserCreateFlags are command line args used when creating a user
type UserCreateFlags struct {
name string
var userCreateFlags struct {
name string
password string
orgID string
}

var userCreateFlags UserCreateFlags

func init() {
userCreateCmd := &cobra.Command{
func userCreateCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "create",
Short: "Create user",
RunE: wrapCheckSetup(userCreateF),
}

userCreateCmd.Flags().StringVarP(&userCreateFlags.name, "name", "n", "", "The user name (required)")
userCreateCmd.MarkFlagRequired("name")
cmd.Flags().StringVarP(&userCreateFlags.name, "name", "n", "", "The user name (required)")
cmd.MarkFlagRequired("name")
cmd.Flags().StringVarP(&userCreateFlags.password, "password", "p", "", "The user password")
cmd.Flags().StringVarP(&userCreateFlags.orgID, "org-id", "o", "", "The organization id the user belongs too. Is required if password provided.")

userCmd.AddCommand(userCreateCmd)
return cmd
}

func userCreateF(cmd *cobra.Command, args []string) error {
Expand All @@ -131,39 +140,84 @@ func userCreateF(cmd *cobra.Command, args []string) error {
return err
}

w := internal.NewTabWriter(os.Stdout)
w.WriteHeaders(
"ID",
"Name",
)
w.Write(map[string]interface{}{
"ID": user.ID.String(),
"Name": user.Name,
writeOutput := func(headers []string, vals ...string) error {
if len(headers) != len(vals) {
return errors.New("invalid headers and val setup for writer")
}

m := make(map[string]interface{})
for i, h := range headers {
m[h] = vals[i]
}
w := internal.NewTabWriter(os.Stdout)
w.WriteHeaders(headers...)
w.Write(m)
w.Flush()

return nil
}

orgIDStr := userCreateFlags.orgID
pass := userCreateFlags.password
if orgIDStr == "" && pass == "" {
return writeOutput([]string{"ID", "Name"}, user.ID.String(), user.Name)
}

if pass != "" && orgIDStr == "" {
return errors.New("an org id is required when providing a user password")
}

orgID, err := platform.IDFromString(orgIDStr)
if err != nil {
return errors.New("an invalid org ID provided: " + orgIDStr)
}

userResMapSVC := &http.UserResourceMappingService{
Addr: flags.host,
Token: flags.token,
InsecureSkipVerify: flags.skipVerify,
}

err = userResMapSVC.CreateUserResourceMapping(context.Background(), &platform.UserResourceMapping{
UserID: user.ID,
UserType: platform.Member,
ResourceType: platform.OrgsResourceType,
ResourceID: *orgID,
})
w.Flush()
if err != nil {
return err
}

return nil
passSVC := &http.PasswordService{
Addr: flags.host,
Token: flags.token,
InsecureSkipVerify: flags.skipVerify,
}

ctx := http.SetUserIDCtx(context.Background(), user.ID.String())
if err := passSVC.SetPassword(ctx, user.Name, pass); err != nil {
return err
}

return writeOutput([]string{"ID", "Name", "Organization ID"}, user.ID.String(), user.Name, orgID.String())
}

// UserFindFlags are command line args used when finding a user
type UserFindFlags struct {
var userFindFlags struct {
id string
name string
}

var userFindFlags UserFindFlags

func init() {
userFindCmd := &cobra.Command{
func userFindCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "find",
Short: "Find user",
RunE: wrapCheckSetup(userFindF),
}

userFindCmd.Flags().StringVarP(&userFindFlags.id, "id", "i", "", "The user ID")
userFindCmd.Flags().StringVarP(&userFindFlags.name, "name", "n", "", "The user name")
cmd.Flags().StringVarP(&userFindFlags.id, "id", "i", "", "The user ID")
cmd.Flags().StringVarP(&userFindFlags.name, "name", "n", "", "The user name")

userCmd.AddCommand(userFindCmd)
return cmd
}

func userFindF(cmd *cobra.Command, args []string) error {
Expand Down Expand Up @@ -205,24 +259,21 @@ func userFindF(cmd *cobra.Command, args []string) error {
return nil
}

// UserDeleteFlags are command line args used when deleting a user
type UserDeleteFlags struct {
var userDeleteFlags struct {
id string
}

var userDeleteFlags UserDeleteFlags

func init() {
userDeleteCmd := &cobra.Command{
func userDeleteCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "delete",
Short: "Delete user",
RunE: wrapCheckSetup(userDeleteF),
}

userDeleteCmd.Flags().StringVarP(&userDeleteFlags.id, "id", "i", "", "The user ID (required)")
userDeleteCmd.MarkFlagRequired("id")
cmd.Flags().StringVarP(&userDeleteFlags.id, "id", "i", "", "The user ID (required)")
cmd.MarkFlagRequired("id")

userCmd.AddCommand(userDeleteCmd)
return cmd
}

func userDeleteF(cmd *cobra.Command, args []string) error {
Expand Down
15 changes: 6 additions & 9 deletions http/authentication_middleware.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package http

import (
"context"
"errors"
"fmt"
"net/http"
"time"
Expand Down Expand Up @@ -89,21 +90,17 @@ func (h *AuthenticationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
}

var auth platform.Authorizer

switch scheme {
case tokenAuthScheme:
auth, err = h.extractAuthorization(ctx, r)
if err != nil {
h.unauthorized(ctx, w, err)
return
}
case sessionAuthScheme:
auth, err = h.extractSession(ctx, r)
if err != nil {
h.unauthorized(ctx, w, err)
return
}
default:
// TODO: this error will be nil if it gets here, this should be remedied with some
// sentinel error I'm thinking
err = errors.New("invalid auth scheme")
}
if err != nil {
h.unauthorized(ctx, w, err)
return
}
Expand Down
Loading

0 comments on commit c8e504c

Please sign in to comment.