1package api 2 3import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "net/url" 9 "os" 10 11 "github.com/hashicorp/errwrap" 12 "github.com/hashicorp/vault/sdk/helper/jsonutil" 13) 14 15const ( 16 wrappedResponseLocation = "cubbyhole/response" 17) 18 19var ( 20 // The default TTL that will be used with `sys/wrapping/wrap`, can be 21 // changed 22 DefaultWrappingTTL = "5m" 23 24 // The default function used if no other function is set, which honors the 25 // env var and wraps `sys/wrapping/wrap` 26 DefaultWrappingLookupFunc = func(operation, path string) string { 27 if os.Getenv(EnvVaultWrapTTL) != "" { 28 return os.Getenv(EnvVaultWrapTTL) 29 } 30 31 if (operation == "PUT" || operation == "POST") && path == "sys/wrapping/wrap" { 32 return DefaultWrappingTTL 33 } 34 35 return "" 36 } 37) 38 39// Logical is used to perform logical backend operations on Vault. 40type Logical struct { 41 c *Client 42} 43 44// Logical is used to return the client for logical-backend API calls. 45func (c *Client) Logical() *Logical { 46 return &Logical{c: c} 47} 48 49func (c *Logical) Read(path string) (*Secret, error) { 50 return c.ReadWithData(path, nil) 51} 52 53func (c *Logical) ReadWithData(path string, data map[string][]string) (*Secret, error) { 54 r := c.c.NewRequest("GET", "/v1/"+path) 55 56 var values url.Values 57 for k, v := range data { 58 if values == nil { 59 values = make(url.Values) 60 } 61 for _, val := range v { 62 values.Add(k, val) 63 } 64 } 65 66 if values != nil { 67 r.Params = values 68 } 69 70 ctx, cancelFunc := context.WithCancel(context.Background()) 71 defer cancelFunc() 72 resp, err := c.c.RawRequestWithContext(ctx, r) 73 if resp != nil { 74 defer resp.Body.Close() 75 } 76 if resp != nil && resp.StatusCode == 404 { 77 secret, parseErr := ParseSecret(resp.Body) 78 switch parseErr { 79 case nil: 80 case io.EOF: 81 return nil, nil 82 default: 83 return nil, err 84 } 85 if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) { 86 return secret, nil 87 } 88 return nil, nil 89 } 90 if err != nil { 91 return nil, err 92 } 93 94 return ParseSecret(resp.Body) 95} 96 97func (c *Logical) List(path string) (*Secret, error) { 98 r := c.c.NewRequest("LIST", "/v1/"+path) 99 // Set this for broader compatibility, but we use LIST above to be able to 100 // handle the wrapping lookup function 101 r.Method = "GET" 102 r.Params.Set("list", "true") 103 104 ctx, cancelFunc := context.WithCancel(context.Background()) 105 defer cancelFunc() 106 resp, err := c.c.RawRequestWithContext(ctx, r) 107 if resp != nil { 108 defer resp.Body.Close() 109 } 110 if resp != nil && resp.StatusCode == 404 { 111 secret, parseErr := ParseSecret(resp.Body) 112 switch parseErr { 113 case nil: 114 case io.EOF: 115 return nil, nil 116 default: 117 return nil, err 118 } 119 if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) { 120 return secret, nil 121 } 122 return nil, nil 123 } 124 if err != nil { 125 return nil, err 126 } 127 128 return ParseSecret(resp.Body) 129} 130 131func (c *Logical) Write(path string, data map[string]interface{}) (*Secret, error) { 132 r := c.c.NewRequest("PUT", "/v1/"+path) 133 if err := r.SetJSONBody(data); err != nil { 134 return nil, err 135 } 136 137 return c.write(path, r) 138} 139 140func (c *Logical) WriteBytes(path string, data []byte) (*Secret, error) { 141 r := c.c.NewRequest("PUT", "/v1/"+path) 142 r.BodyBytes = data 143 144 return c.write(path, r) 145} 146 147func (c *Logical) write(path string, request *Request) (*Secret, error) { 148 ctx, cancelFunc := context.WithCancel(context.Background()) 149 defer cancelFunc() 150 resp, err := c.c.RawRequestWithContext(ctx, request) 151 if resp != nil { 152 defer resp.Body.Close() 153 } 154 if resp != nil && resp.StatusCode == 404 { 155 secret, parseErr := ParseSecret(resp.Body) 156 switch parseErr { 157 case nil: 158 case io.EOF: 159 return nil, nil 160 default: 161 return nil, err 162 } 163 if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) { 164 return secret, err 165 } 166 } 167 if err != nil { 168 return nil, err 169 } 170 171 return ParseSecret(resp.Body) 172} 173 174func (c *Logical) Delete(path string) (*Secret, error) { 175 return c.DeleteWithData(path, nil) 176} 177 178func (c *Logical) DeleteWithData(path string, data map[string][]string) (*Secret, error) { 179 r := c.c.NewRequest("DELETE", "/v1/"+path) 180 181 var values url.Values 182 for k, v := range data { 183 if values == nil { 184 values = make(url.Values) 185 } 186 for _, val := range v { 187 values.Add(k, val) 188 } 189 } 190 191 if values != nil { 192 r.Params = values 193 } 194 195 ctx, cancelFunc := context.WithCancel(context.Background()) 196 defer cancelFunc() 197 resp, err := c.c.RawRequestWithContext(ctx, r) 198 if resp != nil { 199 defer resp.Body.Close() 200 } 201 if resp != nil && resp.StatusCode == 404 { 202 secret, parseErr := ParseSecret(resp.Body) 203 switch parseErr { 204 case nil: 205 case io.EOF: 206 return nil, nil 207 default: 208 return nil, err 209 } 210 if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) { 211 return secret, err 212 } 213 } 214 if err != nil { 215 return nil, err 216 } 217 218 return ParseSecret(resp.Body) 219} 220 221func (c *Logical) Unwrap(wrappingToken string) (*Secret, error) { 222 var data map[string]interface{} 223 if wrappingToken != "" { 224 if c.c.Token() == "" { 225 c.c.SetToken(wrappingToken) 226 } else if wrappingToken != c.c.Token() { 227 data = map[string]interface{}{ 228 "token": wrappingToken, 229 } 230 } 231 } 232 233 r := c.c.NewRequest("PUT", "/v1/sys/wrapping/unwrap") 234 if err := r.SetJSONBody(data); err != nil { 235 return nil, err 236 } 237 238 ctx, cancelFunc := context.WithCancel(context.Background()) 239 defer cancelFunc() 240 resp, err := c.c.RawRequestWithContext(ctx, r) 241 if resp != nil { 242 defer resp.Body.Close() 243 } 244 if resp == nil || resp.StatusCode != 404 { 245 if err != nil { 246 return nil, err 247 } 248 if resp == nil { 249 return nil, nil 250 } 251 return ParseSecret(resp.Body) 252 } 253 254 // In the 404 case this may actually be a wrapped 404 error 255 secret, parseErr := ParseSecret(resp.Body) 256 switch parseErr { 257 case nil: 258 case io.EOF: 259 return nil, nil 260 default: 261 return nil, err 262 } 263 if secret != nil && (len(secret.Warnings) > 0 || len(secret.Data) > 0) { 264 return secret, nil 265 } 266 267 // Otherwise this might be an old-style wrapping token so attempt the old 268 // method 269 if wrappingToken != "" { 270 origToken := c.c.Token() 271 defer c.c.SetToken(origToken) 272 c.c.SetToken(wrappingToken) 273 } 274 275 secret, err = c.Read(wrappedResponseLocation) 276 if err != nil { 277 return nil, errwrap.Wrapf(fmt.Sprintf("error reading %q: {{err}}", wrappedResponseLocation), err) 278 } 279 if secret == nil { 280 return nil, fmt.Errorf("no value found at %q", wrappedResponseLocation) 281 } 282 if secret.Data == nil { 283 return nil, fmt.Errorf("\"data\" not found in wrapping response") 284 } 285 if _, ok := secret.Data["response"]; !ok { 286 return nil, fmt.Errorf("\"response\" not found in wrapping response \"data\" map") 287 } 288 289 wrappedSecret := new(Secret) 290 buf := bytes.NewBufferString(secret.Data["response"].(string)) 291 if err := jsonutil.DecodeJSONFromReader(buf, wrappedSecret); err != nil { 292 return nil, errwrap.Wrapf("error unmarshalling wrapped secret: {{err}}", err) 293 } 294 295 return wrappedSecret, nil 296} 297