Skip to content

Commit

Permalink
Merge pull request nutanix#88 from maxaudron/master
Browse files Browse the repository at this point in the history
implement session based authentification
  • Loading branch information
marinsalinas authored May 15, 2020
2 parents b0b645e + e01ea5c commit 478a843
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 20 deletions.
56 changes: 46 additions & 10 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ type Client struct {
// User agent for client
UserAgent string

Cookies []*http.Cookie

// Optional function called after every successful request made.
onRequestCompleted RequestCompletionCallback
}
Expand All @@ -46,13 +48,14 @@ type RequestCompletionCallback func(*http.Request, *http.Response, interface{})

// Credentials needed username and password
type Credentials struct {
URL string
Username string
Password string
Endpoint string
Port string
Insecure bool
ProxyURL string
URL string
Username string
Password string
Endpoint string
Port string
Insecure bool
SessionAuth bool
ProxyURL string
}

// NewClient returns a new Nutanix API client.
Expand Down Expand Up @@ -82,7 +85,32 @@ func NewClient(credentials *Credentials) (*Client, error) {
return nil, err
}

c := &Client{credentials, httpClient, baseURL, userAgent, nil}
c := &Client{credentials, httpClient, baseURL, userAgent, nil, nil}

if credentials.SessionAuth {
log.Printf("[DEBUG] Using session_auth\n")

ctx := context.TODO()
req, err := c.NewRequest(ctx, http.MethodGet, "/users/me", nil)
if err != nil {
return c, err
}

resp, err := c.client.Do(req)

if err != nil {
return c, err
}
defer func() {
if rerr := resp.Body.Close(); err == nil {
err = rerr
}
}()

err = CheckResponse(resp)

c.Cookies = resp.Cookies()
}

return c, nil
}
Expand Down Expand Up @@ -114,8 +142,16 @@ func (c *Client) NewRequest(ctx context.Context, method, urlStr string, body int
req.Header.Add("Content-Type", mediaType)
req.Header.Add("Accept", mediaType)
req.Header.Add("User-Agent", c.UserAgent)
req.Header.Add("Authorization", "Basic "+
base64.StdEncoding.EncodeToString([]byte(c.Credentials.Username+":"+c.Credentials.Password)))
if c.Cookies != nil {
log.Printf("[DEBUG] Adding cookies to request\n")
for _, i := range c.Cookies {
req.AddCookie(i)
}
} else {
log.Printf("[DEBUG] Adding basic auth to request\n")
req.Header.Add("Authorization", "Basic "+
base64.StdEncoding.EncodeToString([]byte(c.Credentials.Username+":"+c.Credentials.Password)))
}

return req, nil
}
Expand Down
6 changes: 3 additions & 3 deletions client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ func setup() (*http.ServeMux, *Client, *httptest.Server) {
mux := http.NewServeMux()
server := httptest.NewServer(mux)

client, _ := NewClient(&Credentials{"", "username", "password", "", "", true, ""})
client, _ := NewClient(&Credentials{"", "username", "password", "", "", true, false, ""})
client.BaseURL, _ = url.Parse(server.URL)

return mux, client, server
}

func TestNewClient(t *testing.T) {
c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, ""})
c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, ""})

if err != nil {
t.Errorf("Unexpected Error: %v", err)
Expand All @@ -41,7 +41,7 @@ func TestNewClient(t *testing.T) {
}

func TestNewRequest(t *testing.T) {
c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, ""})
c, err := NewClient(&Credentials{"foo.com", "username", "password", "", "", true, false, ""})

if err != nil {
t.Errorf("Unexpected Error: %v", err)
Expand Down
16 changes: 9 additions & 7 deletions nutanix/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,22 @@ type Config struct {
Password string
Port string
Insecure bool
SessionAuth bool
WaitTimeout int64
ProxyURL string
}

// Client ...
func (c *Config) Client() (*Client, error) {
configCreds := client.Credentials{
URL: fmt.Sprintf("%s:%s", c.Endpoint, c.Port),
Endpoint: c.Endpoint,
Username: c.Username,
Password: c.Password,
Port: c.Port,
Insecure: c.Insecure,
ProxyURL: c.ProxyURL,
URL: fmt.Sprintf("%s:%s", c.Endpoint, c.Port),
Endpoint: c.Endpoint,
Username: c.Username,
Password: c.Password,
Port: c.Port,
Insecure: c.Insecure,
SessionAuth: c.SessionAuth,
ProxyURL: c.ProxyURL,
}

v3Client, err := v3.NewV3Client(configCreds)
Expand Down
9 changes: 9 additions & 0 deletions nutanix/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ func Provider() terraform.ResourceProvider {
"insecure": "Explicitly allow the provider to perform \"insecure\" SSL requests. If omitted," +
"default value is `false`",

"session_auth": "Use session authentification instead of basic auth for each request",

"port": "Port for Nutanix Prism.",

"wait_timeout": "Set if you know that the creation o update of a resource may take long time (minutes)",
Expand Down Expand Up @@ -50,6 +52,12 @@ func Provider() terraform.ResourceProvider {
DefaultFunc: schema.EnvDefaultFunc("NUTANIX_INSECURE", false),
Description: descriptions["insecure"],
},
"session_auth": {
Type: schema.TypeBool,
Optional: true,
DefaultFunc: schema.EnvDefaultFunc("NUTANIX_SESSION_AUTH", false),
Description: descriptions["session_auth"],
},
"port": {
Type: schema.TypeString,
Default: "9440",
Expand Down Expand Up @@ -112,6 +120,7 @@ func providerConfigure(d *schema.ResourceData) (interface{}, error) {
Username: d.Get("username").(string),
Password: d.Get("password").(string),
Insecure: d.Get("insecure").(bool),
SessionAuth: d.Get("session_auth").(bool),
Port: d.Get("port").(string),
WaitTimeout: int64(d.Get("wait_timeout").(int)),
ProxyURL: d.Get("proxy_url").(string),
Expand Down
15 changes: 15 additions & 0 deletions website/docs/index.html.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,21 @@ provider "nutanix" {
}
```

### Session based Authentication

Session based authentication can be used which authenticates only once with basic authentication and uses a cookie for all further attempts.
The main benefit is a reduction in the time API calls take to complete. Sessions are only valid for 15 minutes.

Usage:

```hcl
provider "nutanix" {
...
session_auth = true
...
}
```

### Environment variables

You can provide your credentials via environment variables, representing your Nutanix
Expand Down

0 comments on commit 478a843

Please sign in to comment.