Skip to content

Commit

Permalink
Add support for APIKey authentication
Browse files Browse the repository at this point in the history
In order to facilitate connecting to clusters with API Key
(https://www.elastic.co/guide/en/elasticsearch/reference/master/security-api-create-api-key.html),
add the corresponding configuration option. Takes precedence over username/password.
  • Loading branch information
karmi committed Jul 13, 2019
1 parent ddd1954 commit 3b68c0f
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 4 deletions.
3 changes: 3 additions & 0 deletions elasticsearch.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Config struct {
Password string // Password for HTTP Basic Authentication.

CloudID string // Endpoint for the Elastic Service (https://elastic.co/cloud).
APIKey string // Base64-encoded token for authorization; if set, overrides username and password.

Transport http.RoundTripper // The HTTP transport object.
Logger estransport.Logger // The logger object.
}
Expand Down Expand Up @@ -108,6 +110,7 @@ func NewClient(cfg Config) (*Client, error) {
URLs: urls,
Username: cfg.Username,
Password: cfg.Password,
APIKey: cfg.APIKey,

Transport: cfg.Transport,
Logger: cfg.Logger,
Expand Down
16 changes: 14 additions & 2 deletions estransport/estransport.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ type Config struct {
URLs []*url.URL
Username string
Password string
APIKey string

Transport http.RoundTripper
Logger Logger
Expand All @@ -50,6 +51,7 @@ type Client struct {
urls []*url.URL
username string
password string
apikey string

transport http.RoundTripper
selector Selector
Expand All @@ -69,6 +71,7 @@ func New(cfg Config) *Client {
urls: cfg.URLs,
username: cfg.Username,
password: cfg.Password,
apikey: cfg.APIKey,

transport: cfg.Transport,
selector: NewRoundRobinSelector(cfg.URLs...),
Expand All @@ -89,7 +92,7 @@ func (c *Client) Perform(req *http.Request) (*http.Response, error) {
c.setUserAgent(req)

if _, ok := req.Header["Authorization"]; !ok {
c.setBasicAuth(u, req)
c.setAuthorization(u, req)
}

var dupReqBody *bytes.Buffer
Expand Down Expand Up @@ -154,13 +157,22 @@ func (c *Client) setURL(u *url.URL, req *http.Request) *http.Request {
return req
}

func (c *Client) setBasicAuth(u *url.URL, req *http.Request) *http.Request {
func (c *Client) setAuthorization(u *url.URL, req *http.Request) *http.Request {
if u.User != nil {
password, _ := u.User.Password()
req.SetBasicAuth(u.User.Username(), password)
return req
}

if c.apikey != "" {
var b bytes.Buffer
b.Grow(len("APIKey ") + len(c.apikey))
b.WriteString("APIKey ")
b.WriteString(c.apikey)
req.Header.Set("Authorization", b.String())
return req
}

if c.username != "" && c.password != "" {
req.SetBasicAuth(c.username, c.password)
return req
Expand Down
21 changes: 19 additions & 2 deletions estransport/estransport_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func TestTransportPerform(t *testing.T) {
tp := New(Config{URLs: []*url.URL{u}})

req, _ := http.NewRequest("GET", "/", nil)
tp.setBasicAuth(u, req)
tp.setAuthorization(u, req)

username, password, ok := req.BasicAuth()
if !ok {
Expand All @@ -114,7 +114,7 @@ func TestTransportPerform(t *testing.T) {
tp := New(Config{URLs: []*url.URL{u}, Username: "foo", Password: "bar"})

req, _ := http.NewRequest("GET", "/", nil)
tp.setBasicAuth(u, req)
tp.setAuthorization(u, req)

username, password, ok := req.BasicAuth()
if !ok {
Expand All @@ -126,6 +126,23 @@ func TestTransportPerform(t *testing.T) {
}
})

t.Run("Sets APIKey Authentication from configuration", func(t *testing.T) {
u, _ := url.Parse("http://example.com")
tp := New(Config{URLs: []*url.URL{u}, APIKey: "Zm9vYmFy"}) // foobar

req, _ := http.NewRequest("GET", "/", nil)
tp.setAuthorization(u, req)

value := req.Header.Get("Authorization")
if value == "" {
t.Errorf("Expected the request to have the Authorization header set")
}

if value != "APIKey Zm9vYmFy" {
t.Errorf(`Unexpected value for Authorization: want="APIKey Zm9vYmFy", got="%s"`, value)
}
})

t.Run("Sets UserAgent", func(t *testing.T) {
u, _ := url.Parse("http://example.com")
tp := New(Config{URLs: []*url.URL{u}})
Expand Down

0 comments on commit 3b68c0f

Please sign in to comment.