summaryrefslogtreecommitdiff
path: root/vault/userpass_strategy.go
blob: e65617438f544bba5e2fd8b3e78d0654b4560f33 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package vault

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"
	"time"
)

// UserPassAuthStrategy - an AuthStrategy that uses Vault's userpass authentication backend.
type UserPassAuthStrategy struct {
	Username string `json:"-"`
	Password string `json:"password"`
	Mount    string `json:"-"`
	hc       *http.Client
}

// NewUserPassAuthStrategy - create an AuthStrategy that uses Vault's userpass auth
// backend.
func NewUserPassAuthStrategy() *UserPassAuthStrategy {
	username := os.Getenv("VAULT_AUTH_USERNAME")
	password := os.Getenv("VAULT_AUTH_PASSWORD")
	mount := os.Getenv("VAULT_AUTH_USERPASS_MOUNT")
	if mount == "" {
		mount = "userpass"
	}
	if username != "" && password != "" {
		return &UserPassAuthStrategy{username, password, mount, nil}
	}
	return nil
}

// GetHTTPClient configures the HTTP client with a timeout
func (a *UserPassAuthStrategy) GetHTTPClient() *http.Client {
	if a.hc == nil {
		a.hc = &http.Client{Timeout: time.Second * 5}
	}
	return a.hc
}

// SetToken is a no-op for UserPassAuthStrategy as a token hasn't been acquired yet
func (a *UserPassAuthStrategy) SetToken(req *http.Request) {
	// no-op
}

// Do wraps http.Client.Do
func (a *UserPassAuthStrategy) Do(req *http.Request) (*http.Response, error) {
	hc := a.GetHTTPClient()
	return hc.Do(req)
}

// GetToken - log in to the app-id auth backend and return the client token
func (a *UserPassAuthStrategy) GetToken(addr *url.URL) (string, error) {
	buf := new(bytes.Buffer)
	json.NewEncoder(buf).Encode(&a)

	u := &url.URL{}
	*u = *addr
	u.Path = "/v1/auth/" + a.Mount + "/login/" + a.Username
	res, err := requestAndFollow(a, "POST", u, buf.Bytes())
	if err != nil {
		return "", err
	}
	response := &UserPassAuthResponse{}
	err = json.NewDecoder(res.Body).Decode(response)
	res.Body.Close()
	if err != nil {
		return "", err
	}
	if res.StatusCode != 200 {
		err := fmt.Errorf("Unexpected HTTP status %d on AppId login to %s: %s", res.StatusCode, u, response)
		return "", err
	}
	return response.Auth.ClientToken, nil
}

// Revokable -
func (a *UserPassAuthStrategy) Revokable() bool {
	return true
}

func (a *UserPassAuthStrategy) String() string {
	return fmt.Sprintf("username: %s, password: %s, mount: %s", a.Username, a.Password, a.Mount)
}

// UserPassAuthResponse - the Auth response from /v1/auth/username/login
type UserPassAuthResponse struct {
	Auth struct {
		ClientToken   string `json:"client_token"`
		LeaseDuration int64  `json:"lease_duration"`
		Metadata      struct {
			Username string `json:"username"`
		} `json:"metadata"`
		Policies  []string `json:"policies"`
		Renewable bool     `json:"renewable"`
	} `json:"auth"`
}

func (a *UserPassAuthResponse) String() string {
	buf := new(bytes.Buffer)
	json.NewEncoder(buf).Encode(&a)
	return string(buf.Bytes())
}