github.com/govau/cf-common@v0.0.7/credhub/client.go (about) 1 package credhub 2 3 import ( 4 "bytes" 5 "crypto/tls" 6 "crypto/x509" 7 "encoding/json" 8 "errors" 9 "fmt" 10 "io/ioutil" 11 "net/http" 12 "net/url" 13 "time" 14 ) 15 16 type Client struct { 17 ClientID string `yaml:"client_id"` 18 ClientSecret string `yaml:"client_secret"` 19 UAAURL string `yaml:"uaa_url"` // URL to access 20 UAACACerts []string `yaml:"uaa_ca_certificates"` // CA certs for credhub host 21 CredHubURL string `yaml:"credhub_url"` // URL to access 22 CredHubCACerts []string `yaml:"credhub_ca_certificates"` // CA certs for credhub host 23 24 uaaClient *http.Client 25 credHubClient *http.Client 26 token *oauthToken 27 } 28 29 var ( 30 errCredNotFound = credHubErr{errors.New("not found in credhub")} 31 ) 32 33 type credHubErr struct { 34 error 35 } 36 37 func IsCommsRelatedError(err error) bool { 38 _, ok := err.(credHubErr) 39 return ok 40 } 41 42 func IsNotFoundError(err error) bool { 43 return err == errCredNotFound 44 } 45 46 func (c *Client) Init() error { 47 uaaCaCertPool := x509.NewCertPool() 48 credHubCaCertPool := x509.NewCertPool() 49 50 for _, ca := range c.UAACACerts { 51 ok := uaaCaCertPool.AppendCertsFromPEM([]byte(ca)) 52 if !ok { 53 return credHubErr{errors.New("AppendCertsFromPEM was not ok")} 54 } 55 } 56 for _, ca := range c.CredHubCACerts { 57 ok := credHubCaCertPool.AppendCertsFromPEM([]byte(ca)) 58 if !ok { 59 return credHubErr{errors.New("AppendCertsFromPEM was not ok")} 60 } 61 } 62 63 uaaTLS := &tls.Config{RootCAs: uaaCaCertPool} 64 credhubTLS := &tls.Config{RootCAs: credHubCaCertPool} 65 66 uaaTLS.BuildNameToCertificate() 67 credhubTLS.BuildNameToCertificate() 68 69 c.uaaClient = &http.Client{Transport: &http.Transport{TLSClientConfig: uaaTLS}} 70 c.credHubClient = &http.Client{Transport: &http.Transport{TLSClientConfig: credhubTLS}} 71 72 return nil 73 } 74 75 type oauthToken struct { 76 AccessToken string `json:"access_token"` 77 Expiry int64 `json:"expires_in"` 78 } 79 80 func (ch *Client) updateToken() error { 81 if ch.token == nil || time.Unix(ch.token.Expiry, 0).Before(time.Now().Add(5*time.Minute)) { 82 r, err := http.NewRequest(http.MethodPost, ch.UAAURL+"/oauth/token", bytes.NewReader([]byte((&url.Values{ 83 "client_id": {ch.ClientID}, 84 "client_secret": {ch.ClientSecret}, 85 "grant_type": {"client_credentials"}, 86 "response_type": {"token"}, 87 }).Encode()))) 88 if err != nil { 89 return credHubErr{err} 90 } 91 r.Header.Set("Accept", "application/json") 92 r.Header.Set("Content-Type", "application/x-www-form-urlencoded") 93 94 resp, err := ch.uaaClient.Do(r) 95 if err != nil { 96 return credHubErr{err} 97 } 98 data, err := ioutil.ReadAll(resp.Body) 99 resp.Body.Close() 100 if err != nil { 101 return credHubErr{err} 102 } 103 if resp.StatusCode != http.StatusOK { 104 return credHubErr{fmt.Errorf("not OK response from UAA: %s", data)} 105 } 106 107 var at oauthToken 108 err = json.Unmarshal(data, &at) 109 if err != nil { 110 return credHubErr{err} 111 } 112 113 ch.token = &at 114 } 115 116 return nil 117 } 118 119 func (ch *Client) MakeRequest(path string, params url.Values, rv interface{}) error { 120 req, err := http.NewRequest(http.MethodGet, ch.CredHubURL+path+"?"+params.Encode(), nil) 121 if err != nil { 122 return credHubErr{err} 123 } 124 125 return ch.rawMakeRequest(req, rv) 126 } 127 128 func (ch *Client) PutRequest(path string, val, rv interface{}) error { 129 data, err := json.Marshal(val) 130 if err != nil { 131 return credHubErr{err} 132 } 133 134 req, err := http.NewRequest(http.MethodPut, ch.CredHubURL+path, bytes.NewReader(data)) 135 if err != nil { 136 return credHubErr{err} 137 } 138 139 req.Header.Set("Content-Type", "application/json") 140 141 return ch.rawMakeRequest(req, rv) 142 } 143 144 func (ch *Client) DeleteRequest(path string, params url.Values) error { 145 req, err := http.NewRequest(http.MethodDelete, ch.CredHubURL+path+"?"+params.Encode(), nil) 146 if err != nil { 147 return credHubErr{err} 148 } 149 150 req.Header.Set("Content-Type", "application/json") 151 152 return ch.rawMakeRequest(req, nil) 153 } 154 155 func (ch *Client) rawMakeRequest(req *http.Request, rv interface{}) error { 156 err := ch.updateToken() 157 if err != nil { 158 return err 159 } 160 161 req.Header.Set("Authorization", "Bearer "+ch.token.AccessToken) 162 163 resp, err := ch.credHubClient.Do(req) 164 if err != nil { 165 return credHubErr{err} 166 } 167 contents, err := ioutil.ReadAll(resp.Body) 168 resp.Body.Close() 169 if err != nil { 170 return credHubErr{err} 171 } 172 173 switch resp.StatusCode { 174 case http.StatusOK: 175 err = json.Unmarshal(contents, rv) 176 if err != nil { 177 return credHubErr{err} 178 } 179 return nil 180 case http.StatusNoContent: 181 return nil // expected for deleted 182 case http.StatusNotFound: 183 return errCredNotFound 184 default: 185 return credHubErr{fmt.Errorf("not OK response from CredHub: %s", contents)} 186 } 187 }