Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix auth for CLI #2265

Merged
merged 9 commits into from
Apr 13, 2015
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- [#2257](https://github.com/influxdb/influxdb/pull/2257): Add "snapshotting" pseudo state & log entry cache.
- [#2261](https://github.com/influxdb/influxdb/pull/2261): Support int64 value types.
- [#2191](https://github.com/influxdb/influxdb/pull/2191): Case-insensitive check for "fill"
- [#2265](https://github.com/influxdb/influxdb/pull/2265): Fix auth for CLI.

## v0.9.0-rc23 [2015-04-11]

Expand Down
29 changes: 23 additions & 6 deletions client/influxdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,17 @@ func NewClient(c Config) (*Client, error) {
return &client, nil
}

// SetAuth will update the username and passwords
func (c *Client) SetAuth(u, p string) {
c.username = u
c.password = p
}

// Query sends a command to the server and returns the Response
func (c *Client) Query(q Query) (*Response, error) {
u := c.url

u.Path = "query"
if c.username != "" {
u.User = url.UserPassword(c.username, c.password)
}
values := u.Query()
values.Set("q", q.Command)
values.Set("db", q.Database)
Expand All @@ -72,6 +75,10 @@ func (c *Client) Query(q Query) (*Response, error) {
return nil, err
}
req.Header.Set("User-Agent", c.userAgent)
if c.username != "" {
req.SetBasicAuth(c.username, c.password)
}

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
Expand All @@ -81,9 +88,19 @@ func (c *Client) Query(q Query) (*Response, error) {
var response Response
dec := json.NewDecoder(resp.Body)
dec.UseNumber()
err = dec.Decode(&response)
if err != nil {
return nil, err
decErr := dec.Decode(&response)

// ignore this error if we got an invalid status code
if decErr != nil && decErr.Error() == "EOF" && resp.StatusCode != http.StatusOK {
decErr = nil
}
// If we got a valid decode error, send that back
if decErr != nil {
return nil, decErr
}
// If we don't have an error in our json response, and didn't get statusOK, then send back an error
if resp.StatusCode != http.StatusOK && response.Error() == nil {
return &response, fmt.Errorf("received status code %d from server", resp.StatusCode)
}
return &response, nil
}
Expand Down
58 changes: 36 additions & 22 deletions cmd/influx/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ func main() {
}

if c.Execute != "" {
if err := c.executeQuery(c.Execute); err != nil {
if err := c.ExecuteQuery(c.Execute); err != nil {
os.Exit(1)
} else {
os.Exit(0)
Expand Down Expand Up @@ -140,13 +140,13 @@ func (c *CommandLine) ParseCommand(cmd string) bool {
// signal the program to exit
return false
case strings.HasPrefix(lcmd, "gopher"):
gopher()
c.gopher()
case strings.HasPrefix(lcmd, "connect"):
c.connect(cmd)
case strings.HasPrefix(lcmd, "auth"):
c.SetAuth()
c.SetAuth(cmd)
case strings.HasPrefix(lcmd, "help"):
help()
c.help()
case strings.HasPrefix(lcmd, "format"):
c.SetFormat(cmd)
case strings.HasPrefix(lcmd, "settings"):
Expand All @@ -163,7 +163,7 @@ func (c *CommandLine) ParseCommand(cmd string) bool {
case lcmd == "":
break
default:
c.executeQuery(cmd)
c.ExecuteQuery(cmd)
}
return true
}
Expand Down Expand Up @@ -205,9 +205,6 @@ func (c *CommandLine) connect(cmd string) {
} else {
u.Host = c.Host
}
if c.Username != "" {
u.User = url.UserPassword(c.Username, c.Password)
}
cl, err := client.NewClient(
client.Config{
URL: u,
Expand All @@ -230,19 +227,36 @@ func (c *CommandLine) connect(cmd string) {
}
}

func (c *CommandLine) SetAuth() {
u, e := c.Line.Prompt("username: ")
if e != nil {
fmt.Printf("Unable to process input: %s", e)
return
func (c *CommandLine) SetAuth(cmd string) {
// If they pass in the entire command, we should parse it
// auth <username> <password>
args := strings.Fields(cmd)
if len(args) == 3 {
args = args[1:]
} else {
args = []string{}
}
c.Username = strings.TrimSpace(u)
p, e := c.Line.PasswordPrompt("password: ")
if e != nil {
fmt.Printf("Unable to process input: %s", e)
return

if len(args) == 2 {
c.Username = args[0]
c.Password = args[1]
} else {
u, e := c.Line.Prompt("username: ")
if e != nil {
fmt.Printf("Unable to process input: %s", e)
return
}
c.Username = strings.TrimSpace(u)
p, e := c.Line.PasswordPrompt("password: ")
if e != nil {
fmt.Printf("Unable to process input: %s", e)
return
}
c.Password = p
}
c.Password = p

// Update the client as well
c.Client.SetAuth(c.Username, c.Password)
}

func (c *CommandLine) use(cmd string) {
Expand Down Expand Up @@ -286,7 +300,7 @@ func (c *CommandLine) dump() error {
return nil
}

func (c *CommandLine) executeQuery(query string) error {
func (c *CommandLine) ExecuteQuery(query string) error {
response, err := c.Client.Query(client.Query{Command: query, Database: c.Database})
if err != nil {
fmt.Printf("ERR: %s\n", err)
Expand Down Expand Up @@ -473,7 +487,7 @@ func (c *CommandLine) Settings() {
w.Flush()
}

func help() {
func (c *CommandLine) help() {
fmt.Println(`Usage:
connect <host:port> connect to another node
auth prompt for username and password
Expand All @@ -494,7 +508,7 @@ func help() {
`)
}

func gopher() {
func (c *CommandLine) gopher() {
fmt.Println(`
.-::-::://:-::- .:/++/'
'://:-''/oo+//++o+/.://o- ./+:
Expand Down
165 changes: 165 additions & 0 deletions cmd/influx/main_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
package main_test

import (
"fmt"
"io/ioutil"
"net/url"
"os"
"path/filepath"
"testing"

"github.com/influxdb/influxdb/client"
main "github.com/influxdb/influxdb/cmd/influx"
influxd "github.com/influxdb/influxdb/cmd/influxd"
)

func TestParseCommand_CommandsExist(t *testing.T) {
Expand Down Expand Up @@ -75,3 +82,161 @@ func TestParseCommand_Use(t *testing.T) {
}
}
}

func TestQuery_NoAuth(t *testing.T) {
if testing.Short() {
t.Skip("skipping TestQuery_NoAuth")
}

// Create root path to server.
// Remove it to clean up past failed panics
// Defer it to clean up for successful tests
path := tempfile()
os.Remove(path)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tempfile() already removes the path, and cleaning up past runs is not possible, since the path changes each time.

defer os.Remove(path)

config, _ := influxd.NewTestConfig()

// Start server.
node, err := newNode(config, path)
if err != nil {
t.Fatal(err)
}

c := main.CommandLine{
Format: "column",
}

u := url.URL{
Scheme: "http",
Host: fmt.Sprintf("%s:%d", "localhost", 8086),
}
cl, err := client.NewClient(
client.Config{
URL: u,
})

if err != nil {
t.Fatal(err)
}

c.Client = cl

ok := c.ParseCommand("CREATE USER admin WITH PASSWORD 'password'")
if !ok {
t.Fatal("Failed to create user")
}
node.Close()
}

func TestQuery_Auth(t *testing.T) {
if testing.Short() {
t.Skip("skipping TestQuery_Auth")
}

// Create root path to server.
// Remove it to clean up past failed panics
// Defer it to clean up for successful tests
path := tempfile()
os.Remove(path)
defer os.Remove(path)

// Create the cli
c := main.CommandLine{Format: "column"}
cl, err := client.NewClient(
client.Config{
URL: url.URL{
Scheme: "http",
Host: fmt.Sprintf("%s:%d", "localhost", 8086),
}})
if err != nil {
t.Fatal(err)
}

c.Client = cl

// spin up a server
config, _ := influxd.NewTestConfig()
config.Authentication.Enabled = true
node, err := newNode(config, path)
if err != nil {
t.Fatal(err)
}

// Check to make sure we can't do anything
err = c.ExecuteQuery("CREATE USER admin WITH PASSWORD 'password'")
if err == nil {
t.Fatal("Should have failed to create user")
}

// spin down server
node.Close()

// disable auth and spin back up
config.Authentication.Enabled = false
node, err = newNode(config, path)
if err != nil {
t.Fatal(err)
}

// create the user
err = c.ExecuteQuery("CREATE USER admin WITH PASSWORD 'password'")
if err != nil {
t.Fatalf("Should have created user: %s\n", err)
}
// Make cluster admin
err = c.ExecuteQuery("GRANT ALL PRIVILEGES TO admin")
if err != nil {
t.Fatalf("Should have made cluster admin: %s\n", err)
}

// spin down again
node.Close()

// enable auth, spin back up
config.Authentication.Enabled = true
node, err = newNode(config, path)
if err != nil {
t.Fatal(err)
}

// Check to make sure we still can't do anything
err = c.ExecuteQuery("CREATE USER admin WITH PASSWORD 'password'")
if err == nil {
t.Fatal("Should have failed to create user")
}

c.SetAuth("auth admin password")

// Check to make sure we can't do anything
err = c.ExecuteQuery("CREATE DATABASE foo")
if err != nil {
t.Fatalf("Failed to create database: %s\n", err)
}
node.Close()
}

// tempfile returns a temporary path.
func tempfile() string {
f, _ := ioutil.TempFile("", "influxdb-")
path := f.Name()
f.Close()
os.Remove(path)
return path
}

func newNode(config *influxd.Config, path string) (*influxd.Node, error) {
config.Broker.Dir = filepath.Join(path, "broker")
config.Data.Dir = filepath.Join(path, "data")

// Start server.
cmd := influxd.NewRunCommand()

node := cmd.Open(config, "")
if node.Broker == nil {
return nil, fmt.Errorf("cannot run broker")
} else if node.DataNode == nil {
return nil, fmt.Errorf("cannot run server")
}
return node, nil
}
18 changes: 15 additions & 3 deletions cmd/influxd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,27 @@ func execConfig(args []string) {
}

var (
configPath = fs.String("config", "", "")
hostname = fs.String("hostname", "", "")
configPath string
hostname string
)
fs.StringVar(&configPath, "config", "", "")
fs.StringVar(&hostname, "hostname", "", "")
fs.Parse(args)

config, err := parseConfig(*configPath, *hostname)
var config *Config
var err error
if configPath == "" {
config, err = NewTestConfig()
} else {
config, err = ParseConfigFile(configPath)
}
if err != nil {
log.Fatalf("parse config: %s", err)
}
// Override config properties.
if hostname != "" {
config.Hostname = hostname
}

config.Write(os.Stdout)
}
Expand Down
Loading