mirror of
https://github.com/ceph/ceph-csi.git
synced 2024-11-19 21:00:24 +00:00
rebase: bump github.com/hashicorp/vault/api from 1.6.0 to 1.7.2
Bumps [github.com/hashicorp/vault/api](https://github.com/hashicorp/vault) from 1.6.0 to 1.7.2. - [Release notes](https://github.com/hashicorp/vault/releases) - [Changelog](https://github.com/hashicorp/vault/blob/main/CHANGELOG.md) - [Commits](https://github.com/hashicorp/vault/compare/v1.6.0...v1.7.2) --- updated-dependencies: - dependency-name: github.com/hashicorp/vault/api dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
parent
507844c9b1
commit
acaf19c66e
6
go.mod
6
go.mod
@ -15,7 +15,7 @@ require (
|
||||
github.com/golang/protobuf v1.5.2
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
|
||||
github.com/hashicorp/vault/api v1.6.0
|
||||
github.com/hashicorp/vault/api v1.7.2
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.11.0
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v6 v6.0.1
|
||||
github.com/libopenstorage/secrets v0.0.0-20210908194121-a1d19aa9713a
|
||||
@ -86,7 +86,7 @@ require (
|
||||
github.com/hashicorp/go-retryablehttp v0.6.6 // indirect
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 // indirect
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.2 // indirect
|
||||
@ -94,7 +94,7 @@ require (
|
||||
github.com/hashicorp/golang-lru v0.5.4 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hashicorp/vault v1.4.2 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.5.0 // indirect
|
||||
github.com/hashicorp/vault/sdk v0.5.1 // indirect
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d // indirect
|
||||
github.com/imdario/mergo v0.3.12 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.0 // indirect
|
||||
|
12
go.sum
12
go.sum
@ -593,8 +593,8 @@ github.com/hashicorp/go-secure-stdlib/base62 v0.1.1/go.mod h1:EdWO6czbmthiwZ3/PU
|
||||
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1 h1:cCRo8gK7oq6A2L6LICkUZ+/a5rLiRXFMf1Qd4xSwxTc=
|
||||
github.com/hashicorp/go-secure-stdlib/mlock v0.1.1/go.mod h1:zq93CJChV6L9QTfGKtfBxKqD7BqqXx5O04A/ns2p5+I=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.1/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5 h1:MBgwAFPUbfuI0+tmDU/aeM1MARvdbqWmiieXIalKqDE=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6 h1:om4Al8Oy7kCm/B86rLCLah4Dt5Aa0Fr5rYBG60OzwHQ=
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
|
||||
github.com/hashicorp/go-secure-stdlib/password v0.1.1 h1:6JzmBqXprakgFEHwBgdchsjaA9x3GyjdI568bXKxa60=
|
||||
github.com/hashicorp/go-secure-stdlib/password v0.1.1/go.mod h1:9hH302QllNwu1o2TGYtSk8I8kTAN0ca1EHpwhm5Mmzo=
|
||||
github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
|
||||
@ -664,8 +664,8 @@ github.com/hashicorp/vault/api v1.0.5-0.20191122173911-80fcc7907c78/go.mod h1:Uf
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200215224050-f6547fa8e820/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200317185738-82f498082f02/go.mod h1:3f12BMfgDGjTsTtIUj+ZKZwSobQpZtYGFIEehOv5z1o=
|
||||
github.com/hashicorp/vault/api v1.0.5-0.20200902155336-f9d5ce5a171a/go.mod h1:R3Umvhlxi2TN7Ex2hzOowyeNb+SfbVWI973N+ctaFMk=
|
||||
github.com/hashicorp/vault/api v1.6.0 h1:B8UUYod1y1OoiGHq9GtpiqSnGOUEWHaA26AY8RQEDY4=
|
||||
github.com/hashicorp/vault/api v1.6.0/go.mod h1:h1K70EO2DgnBaTz5IsL6D5ERsNt5Pce93ueVS2+t0Xc=
|
||||
github.com/hashicorp/vault/api v1.7.2 h1:kawHE7s/4xwrdKbkmwQi0wYaIeUhk5ueek7ljuezCVQ=
|
||||
github.com/hashicorp/vault/api v1.7.2/go.mod h1:xbfA+1AvxFseDzxxdWaL0uO99n1+tndus4GCrtouy0M=
|
||||
github.com/hashicorp/vault/sdk v0.1.8/go.mod h1:tHZfc6St71twLizWNHvnnbiGFo1aq0eD2jGPLtP8kAU=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20190730042320-0dc007d98cc8/go.mod h1:B+hVj7TpuQY1Y/GPbCpffmgd+tSEwvhkWnjtSYCaS2M=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20191108161836-82f2b5571044/go.mod h1:PcekaFGiPJyHnFy+NZhP6ll650zEw51Ag7g/YEa+EOU=
|
||||
@ -675,8 +675,8 @@ github.com/hashicorp/vault/sdk v0.1.14-0.20200317185738-82f498082f02/go.mod h1:W
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200427170607-03332aaf8d18/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200429182704-29fce8f27ce4/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/vault/sdk v0.1.14-0.20200519221838-e0cfd64bc267/go.mod h1:WX57W2PwkrOPQ6rVQk+dy5/htHIaB4aBM70EwKThu10=
|
||||
github.com/hashicorp/vault/sdk v0.5.0 h1:EED7p0OCU3OY5SAqJwSANofY1YKMytm+jDHDQ2EzGVQ=
|
||||
github.com/hashicorp/vault/sdk v0.5.0/go.mod h1:UJZHlfwj7qUJG8g22CuxUgkdJouFrBNvBHCyx8XAPdo=
|
||||
github.com/hashicorp/vault/sdk v0.5.1 h1:zly/TmNgOXCGgWIRA8GojyXzG817POtVh3uzIwzZx+8=
|
||||
github.com/hashicorp/vault/sdk v0.5.1/go.mod h1:DoGraE9kKGNcVgPmTuX357Fm6WAx1Okvde8Vp3dPDoU=
|
||||
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
|
||||
github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
|
||||
|
2
vendor/github.com/hashicorp/go-secure-stdlib/parseutil/parseutil.go
generated
vendored
2
vendor/github.com/hashicorp/go-secure-stdlib/parseutil/parseutil.go
generated
vendored
@ -493,7 +493,7 @@ func SafeParseIntSlice(in interface{}, elements int) ([]int, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var result = make([]int, len(raw))
|
||||
var result = make([]int, 0, len(raw))
|
||||
for _, element := range raw {
|
||||
result = append(result, int(element))
|
||||
}
|
||||
|
50
vendor/github.com/hashicorp/vault/api/kv.go
generated
vendored
Normal file
50
vendor/github.com/hashicorp/vault/api/kv.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package api
|
||||
|
||||
// A KVSecret is a key-value secret returned by Vault's KV secrets engine,
|
||||
// and is the most basic type of secret stored in Vault.
|
||||
//
|
||||
// Data contains the key-value pairs of the secret itself,
|
||||
// while Metadata contains a subset of metadata describing
|
||||
// this particular version of the secret.
|
||||
// The Metadata field for a KV v1 secret will always be nil, as
|
||||
// metadata is only supported starting in KV v2.
|
||||
//
|
||||
// The Raw field can be inspected for information about the lease,
|
||||
// and passed to a LifetimeWatcher object for periodic renewal.
|
||||
type KVSecret struct {
|
||||
Data map[string]interface{}
|
||||
VersionMetadata *KVVersionMetadata
|
||||
CustomMetadata map[string]interface{}
|
||||
Raw *Secret
|
||||
}
|
||||
|
||||
// KVv1 is used to return a client for reads and writes against
|
||||
// a KV v1 secrets engine in Vault.
|
||||
//
|
||||
// The mount path is the location where the target KV secrets engine resides
|
||||
// in Vault.
|
||||
//
|
||||
// While v1 is not necessarily deprecated, Vault development servers tend to
|
||||
// use v2 as the version of the KV secrets engine, as this is what's mounted
|
||||
// by default when a server is started in -dev mode. See the kvv2 struct.
|
||||
//
|
||||
// Learn more about the KV secrets engine here:
|
||||
// https://www.vaultproject.io/docs/secrets/kv
|
||||
func (c *Client) KVv1(mountPath string) *KVv1 {
|
||||
return &KVv1{c: c, mountPath: mountPath}
|
||||
}
|
||||
|
||||
// KVv2 is used to return a client for reads and writes against
|
||||
// a KV v2 secrets engine in Vault.
|
||||
//
|
||||
// The mount path is the location where the target KV secrets engine resides
|
||||
// in Vault.
|
||||
//
|
||||
// Vault development servers tend to have "secret" as the mount path,
|
||||
// as these are the default settings when a server is started in -dev mode.
|
||||
//
|
||||
// Learn more about the KV secrets engine here:
|
||||
// https://www.vaultproject.io/docs/secrets/kv
|
||||
func (c *Client) KVv2(mountPath string) *KVv2 {
|
||||
return &KVv2{c: c, mountPath: mountPath}
|
||||
}
|
57
vendor/github.com/hashicorp/vault/api/kv_v1.go
generated
vendored
Normal file
57
vendor/github.com/hashicorp/vault/api/kv_v1.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type KVv1 struct {
|
||||
c *Client
|
||||
mountPath string
|
||||
}
|
||||
|
||||
// Get returns a secret from the KV v1 secrets engine.
|
||||
func (kv *KVv1) Get(ctx context.Context, secretPath string) (*KVSecret, error) {
|
||||
pathToRead := fmt.Sprintf("%s/%s", kv.mountPath, secretPath)
|
||||
|
||||
secret, err := kv.c.Logical().ReadWithContext(ctx, pathToRead)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encountered while reading secret at %s: %w", pathToRead, err)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, fmt.Errorf("no secret found at %s", pathToRead)
|
||||
}
|
||||
|
||||
return &KVSecret{
|
||||
Data: secret.Data,
|
||||
VersionMetadata: nil,
|
||||
Raw: secret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Put inserts a key-value secret (e.g. {"password": "Hashi123"}) into the
|
||||
// KV v1 secrets engine.
|
||||
//
|
||||
// If the secret already exists, it will be overwritten.
|
||||
func (kv *KVv1) Put(ctx context.Context, secretPath string, data map[string]interface{}) error {
|
||||
pathToWriteTo := fmt.Sprintf("%s/%s", kv.mountPath, secretPath)
|
||||
|
||||
_, err := kv.c.Logical().WriteWithContext(ctx, pathToWriteTo, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing secret to %s: %w", pathToWriteTo, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes a secret from the KV v1 secrets engine.
|
||||
func (kv *KVv1) Delete(ctx context.Context, secretPath string) error {
|
||||
pathToDelete := fmt.Sprintf("%s/%s", kv.mountPath, secretPath)
|
||||
|
||||
_, err := kv.c.Logical().DeleteWithContext(ctx, pathToDelete)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting secret at %s: %w", pathToDelete, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
788
vendor/github.com/hashicorp/vault/api/kv_v2.go
generated
vendored
Normal file
788
vendor/github.com/hashicorp/vault/api/kv_v2.go
generated
vendored
Normal file
@ -0,0 +1,788 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
type KVv2 struct {
|
||||
c *Client
|
||||
mountPath string
|
||||
}
|
||||
|
||||
// KVMetadata is the full metadata for a given KV v2 secret.
|
||||
type KVMetadata struct {
|
||||
CASRequired bool `mapstructure:"cas_required"`
|
||||
CreatedTime time.Time `mapstructure:"created_time"`
|
||||
CurrentVersion int `mapstructure:"current_version"`
|
||||
CustomMetadata map[string]interface{} `mapstructure:"custom_metadata"`
|
||||
DeleteVersionAfter time.Duration `mapstructure:"delete_version_after"`
|
||||
MaxVersions int `mapstructure:"max_versions"`
|
||||
OldestVersion int `mapstructure:"oldest_version"`
|
||||
UpdatedTime time.Time `mapstructure:"updated_time"`
|
||||
// Keys are stringified ints, e.g. "3". To get a sorted slice of version metadata, use GetVersionsAsList.
|
||||
Versions map[string]KVVersionMetadata `mapstructure:"versions"`
|
||||
Raw *Secret
|
||||
}
|
||||
|
||||
// KVMetadataPutInput is the subset of metadata that can be replaced for a
|
||||
// KV v2 secret using the PutMetadata method.
|
||||
//
|
||||
// All fields should be explicitly provided, as any fields left unset in the
|
||||
// struct will be reset to their zero value.
|
||||
type KVMetadataPutInput struct {
|
||||
CASRequired bool
|
||||
CustomMetadata map[string]interface{}
|
||||
DeleteVersionAfter time.Duration
|
||||
MaxVersions int
|
||||
}
|
||||
|
||||
// KVMetadataPatchInput is the subset of metadata that can be manually modified for
|
||||
// a KV v2 secret using the PatchMetadata method.
|
||||
//
|
||||
// The struct's fields are all pointers. A pointer to a field's zero
|
||||
// value (e.g. false for *bool) implies that field should be reset to its
|
||||
// zero value after update, whereas a field left as a nil pointer
|
||||
// (e.g. nil for *bool) implies the field should remain unchanged.
|
||||
//
|
||||
// Since maps are already pointers, use an empty map to remove all
|
||||
// custom metadata.
|
||||
type KVMetadataPatchInput struct {
|
||||
CASRequired *bool
|
||||
CustomMetadata map[string]interface{}
|
||||
DeleteVersionAfter *time.Duration
|
||||
MaxVersions *int
|
||||
}
|
||||
|
||||
// KVVersionMetadata is a subset of metadata for a given version of a KV v2 secret.
|
||||
type KVVersionMetadata struct {
|
||||
Version int `mapstructure:"version"`
|
||||
CreatedTime time.Time `mapstructure:"created_time"`
|
||||
DeletionTime time.Time `mapstructure:"deletion_time"`
|
||||
Destroyed bool `mapstructure:"destroyed"`
|
||||
}
|
||||
|
||||
// Currently supported options: WithOption, WithCheckAndSet, WithMethod
|
||||
type KVOption func() (key string, value interface{})
|
||||
|
||||
const (
|
||||
KVOptionCheckAndSet = "cas"
|
||||
KVOptionMethod = "method"
|
||||
KVMergeMethodPatch = "patch"
|
||||
KVMergeMethodReadWrite = "rw"
|
||||
)
|
||||
|
||||
// WithOption can optionally be passed to provide generic options for a
|
||||
// KV request. Valid keys and values depend on the type of request.
|
||||
func WithOption(key string, value interface{}) KVOption {
|
||||
return func() (string, interface{}) {
|
||||
return key, value
|
||||
}
|
||||
}
|
||||
|
||||
// WithCheckAndSet can optionally be passed to perform a check-and-set
|
||||
// operation on a KV request. If not set, the write will be allowed.
|
||||
// If cas is set to 0, a write will only be allowed if the key doesn't exist.
|
||||
// If set to non-zero, the write will only be allowed if the key’s current
|
||||
// version matches the version specified in the cas parameter.
|
||||
func WithCheckAndSet(cas int) KVOption {
|
||||
return WithOption(KVOptionCheckAndSet, cas)
|
||||
}
|
||||
|
||||
// WithMergeMethod can optionally be passed to dictate which type of
|
||||
// patch to perform in a Patch request. If set to "patch", then an HTTP PATCH
|
||||
// request will be issued. If set to "rw", then a read will be performed,
|
||||
// then a local update, followed by a remote update. Defaults to "patch".
|
||||
func WithMergeMethod(method string) KVOption {
|
||||
return WithOption(KVOptionMethod, method)
|
||||
}
|
||||
|
||||
// Get returns the latest version of a secret from the KV v2 secrets engine.
|
||||
//
|
||||
// If the latest version has been deleted, an error will not be thrown, but
|
||||
// the Data field on the returned secret will be nil, and the Metadata field
|
||||
// will contain the deletion time.
|
||||
func (kv *KVv2) Get(ctx context.Context, secretPath string) (*KVSecret, error) {
|
||||
pathToRead := fmt.Sprintf("%s/data/%s", kv.mountPath, secretPath)
|
||||
|
||||
secret, err := kv.c.Logical().ReadWithContext(ctx, pathToRead)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error encountered while reading secret at %s: %w", pathToRead, err)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, fmt.Errorf("no secret found at %s", pathToRead)
|
||||
}
|
||||
|
||||
kvSecret, err := extractDataAndVersionMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing secret at %s: %w", pathToRead, err)
|
||||
}
|
||||
|
||||
cm, err := extractCustomMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading custom metadata for secret at %s: %w", pathToRead, err)
|
||||
}
|
||||
kvSecret.CustomMetadata = cm
|
||||
|
||||
return kvSecret, nil
|
||||
}
|
||||
|
||||
// GetVersion returns the data and metadata for a specific version of the
|
||||
// given secret.
|
||||
//
|
||||
// If that version has been deleted, the Data field on the
|
||||
// returned secret will be nil, and the Metadata field will contain the deletion time.
|
||||
//
|
||||
// GetVersionsAsList can provide a list of available versions sorted by
|
||||
// version number, while the response from GetMetadata contains them as a map.
|
||||
func (kv *KVv2) GetVersion(ctx context.Context, secretPath string, version int) (*KVSecret, error) {
|
||||
pathToRead := fmt.Sprintf("%s/data/%s", kv.mountPath, secretPath)
|
||||
|
||||
queryParams := map[string][]string{"version": {strconv.Itoa(version)}}
|
||||
secret, err := kv.c.Logical().ReadWithDataWithContext(ctx, pathToRead, queryParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, fmt.Errorf("no secret with version %d found at %s", version, pathToRead)
|
||||
}
|
||||
|
||||
kvSecret, err := extractDataAndVersionMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error parsing secret at %s: %w", pathToRead, err)
|
||||
}
|
||||
|
||||
cm, err := extractCustomMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading custom metadata for secret at %s: %w", pathToRead, err)
|
||||
}
|
||||
kvSecret.CustomMetadata = cm
|
||||
|
||||
return kvSecret, nil
|
||||
}
|
||||
|
||||
// GetVersionsAsList returns a subset of the metadata for each version of the secret, sorted by version number.
|
||||
func (kv *KVv2) GetVersionsAsList(ctx context.Context, secretPath string) ([]KVVersionMetadata, error) {
|
||||
pathToRead := fmt.Sprintf("%s/metadata/%s", kv.mountPath, secretPath)
|
||||
|
||||
secret, err := kv.c.Logical().ReadWithContext(ctx, pathToRead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
return nil, fmt.Errorf("no secret metadata found at %s", pathToRead)
|
||||
}
|
||||
|
||||
md, err := extractFullMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to extract metadata from secret to determine versions: %w", err)
|
||||
}
|
||||
|
||||
versionsList := make([]KVVersionMetadata, 0, len(md.Versions))
|
||||
for _, versionMetadata := range md.Versions {
|
||||
versionsList = append(versionsList, versionMetadata)
|
||||
}
|
||||
|
||||
sort.Slice(versionsList, func(i, j int) bool { return versionsList[i].Version < versionsList[j].Version })
|
||||
return versionsList, nil
|
||||
}
|
||||
|
||||
// GetMetadata returns the full metadata for a given secret, including a map of
|
||||
// its existing versions and their respective creation/deletion times, etc.
|
||||
func (kv *KVv2) GetMetadata(ctx context.Context, secretPath string) (*KVMetadata, error) {
|
||||
pathToRead := fmt.Sprintf("%s/metadata/%s", kv.mountPath, secretPath)
|
||||
|
||||
secret, err := kv.c.Logical().ReadWithContext(ctx, pathToRead)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if secret == nil || secret.Data == nil {
|
||||
return nil, fmt.Errorf("no secret metadata found at %s", pathToRead)
|
||||
}
|
||||
|
||||
md, err := extractFullMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to extract metadata from secret: %w", err)
|
||||
}
|
||||
|
||||
return md, nil
|
||||
}
|
||||
|
||||
// Put inserts a key-value secret (e.g. {"password": "Hashi123"})
|
||||
// into the KV v2 secrets engine.
|
||||
//
|
||||
// If the secret already exists, a new version will be created
|
||||
// and the previous version can be accessed with the GetVersion method.
|
||||
// GetMetadata can provide a list of available versions.
|
||||
func (kv *KVv2) Put(ctx context.Context, secretPath string, data map[string]interface{}, opts ...KVOption) (*KVSecret, error) {
|
||||
pathToWriteTo := fmt.Sprintf("%s/data/%s", kv.mountPath, secretPath)
|
||||
|
||||
wrappedData := map[string]interface{}{
|
||||
"data": data,
|
||||
}
|
||||
|
||||
// Add options such as check-and-set, etc.
|
||||
// We leave this as an optional arg so that most users
|
||||
// can just pass plain key-value secret data without
|
||||
// having to remember to put the extra layer "data" in there.
|
||||
options := make(map[string]interface{})
|
||||
for _, opt := range opts {
|
||||
k, v := opt()
|
||||
options[k] = v
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
wrappedData["options"] = options
|
||||
}
|
||||
|
||||
secret, err := kv.c.Logical().WriteWithContext(ctx, pathToWriteTo, wrappedData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error writing secret to %s: %w", pathToWriteTo, err)
|
||||
}
|
||||
if secret == nil {
|
||||
return nil, fmt.Errorf("no secret was written to %s", pathToWriteTo)
|
||||
}
|
||||
|
||||
metadata, err := extractVersionMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("secret was written successfully, but unable to view version metadata from response: %w", err)
|
||||
}
|
||||
|
||||
kvSecret := &KVSecret{
|
||||
Data: nil, // secret.Data in this case is the metadata
|
||||
VersionMetadata: metadata,
|
||||
Raw: secret,
|
||||
}
|
||||
|
||||
cm, err := extractCustomMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading custom metadata for secret at %s: %w", pathToWriteTo, err)
|
||||
}
|
||||
kvSecret.CustomMetadata = cm
|
||||
|
||||
return kvSecret, nil
|
||||
}
|
||||
|
||||
// PutMetadata can be used to fully replace a subset of metadata fields for a
|
||||
// given KV v2 secret. All fields will replace the corresponding values on the Vault server.
|
||||
// Any fields left as nil will reset the field on the Vault server back to its zero value.
|
||||
//
|
||||
// To only partially replace the values of these metadata fields, use PatchMetadata.
|
||||
//
|
||||
// This method can also be used to create a new secret with just metadata and no secret data yet.
|
||||
func (kv *KVv2) PutMetadata(ctx context.Context, secretPath string, metadata KVMetadataPutInput) error {
|
||||
pathToWriteTo := fmt.Sprintf("%s/metadata/%s", kv.mountPath, secretPath)
|
||||
|
||||
const (
|
||||
casRequiredKey = "cas_required"
|
||||
deleteVersionAfterKey = "delete_version_after"
|
||||
maxVersionsKey = "max_versions"
|
||||
customMetadataKey = "custom_metadata"
|
||||
)
|
||||
|
||||
// convert values to a map we can pass to Logical
|
||||
metadataMap := make(map[string]interface{})
|
||||
metadataMap[maxVersionsKey] = metadata.MaxVersions
|
||||
metadataMap[deleteVersionAfterKey] = metadata.DeleteVersionAfter.String()
|
||||
metadataMap[casRequiredKey] = metadata.CASRequired
|
||||
metadataMap[customMetadataKey] = metadata.CustomMetadata
|
||||
|
||||
_, err := kv.c.Logical().WriteWithContext(ctx, pathToWriteTo, metadataMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error writing secret metadata to %s: %w", pathToWriteTo, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Patch additively updates the most recent version of a key-value secret,
|
||||
// differentiating it from Put which will fully overwrite the previous data.
|
||||
// Only the key-value pairs that are new or changing need to be provided.
|
||||
//
|
||||
// The WithMethod KVOption function can optionally be passed to dictate which
|
||||
// kind of patch to perform, as older Vault server versions (pre-1.9.0) may
|
||||
// only be able to use the old "rw" (read-then-write) style of partial update,
|
||||
// whereas newer Vault servers can use the default value of "patch" if the
|
||||
// client token's policy has the "patch" capability.
|
||||
func (kv *KVv2) Patch(ctx context.Context, secretPath string, newData map[string]interface{}, opts ...KVOption) (*KVSecret, error) {
|
||||
// determine patch method
|
||||
var patchMethod string
|
||||
var ok bool
|
||||
for _, opt := range opts {
|
||||
k, v := opt()
|
||||
if k == "method" {
|
||||
patchMethod, ok = v.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unsupported type provided for option value; value for patch method should be string \"rw\" or \"patch\"")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which kind of patch to use,
|
||||
// the newer HTTP Patch style or the older read-then-write style
|
||||
var kvs *KVSecret
|
||||
var perr error
|
||||
switch patchMethod {
|
||||
case "rw":
|
||||
kvs, perr = readThenWrite(ctx, kv.c, kv.mountPath, secretPath, newData)
|
||||
case "patch":
|
||||
kvs, perr = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
|
||||
case "":
|
||||
kvs, perr = mergePatch(ctx, kv.c, kv.mountPath, secretPath, newData, opts...)
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported patch method provided; value for patch method should be string \"rw\" or \"patch\"")
|
||||
}
|
||||
if perr != nil {
|
||||
return nil, fmt.Errorf("unable to perform patch: %w", perr)
|
||||
}
|
||||
if kvs == nil {
|
||||
return nil, fmt.Errorf("no secret was written to %s", secretPath)
|
||||
}
|
||||
|
||||
return kvs, nil
|
||||
}
|
||||
|
||||
// PatchMetadata can be used to replace just a subset of a secret's
|
||||
// metadata fields at a time, as opposed to PutMetadata which is used to
|
||||
// completely replace all fields on the previous metadata.
|
||||
func (kv *KVv2) PatchMetadata(ctx context.Context, secretPath string, metadata KVMetadataPatchInput) error {
|
||||
pathToWriteTo := fmt.Sprintf("%s/metadata/%s", kv.mountPath, secretPath)
|
||||
|
||||
md, err := toMetadataMap(metadata)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to create map for JSON merge patch request: %w", err)
|
||||
}
|
||||
|
||||
_, err = kv.c.Logical().JSONMergePatch(ctx, pathToWriteTo, md)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error patching metadata at %s: %w", pathToWriteTo, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Delete deletes the most recent version of a secret from the KV v2
|
||||
// secrets engine. To delete an older version, use DeleteVersions.
|
||||
func (kv *KVv2) Delete(ctx context.Context, secretPath string) error {
|
||||
pathToDelete := fmt.Sprintf("%s/data/%s", kv.mountPath, secretPath)
|
||||
|
||||
_, err := kv.c.Logical().DeleteWithContext(ctx, pathToDelete)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting secret at %s: %w", pathToDelete, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteVersions deletes the specified versions of a secret from the KV v2
|
||||
// secrets engine. To delete the latest version of a secret, just use Delete.
|
||||
func (kv *KVv2) DeleteVersions(ctx context.Context, secretPath string, versions []int) error {
|
||||
// verb and path are different when trying to delete past versions
|
||||
pathToDelete := fmt.Sprintf("%s/delete/%s", kv.mountPath, secretPath)
|
||||
|
||||
if len(versions) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
var versionsToDelete []string
|
||||
for _, version := range versions {
|
||||
versionsToDelete = append(versionsToDelete, strconv.Itoa(version))
|
||||
}
|
||||
versionsMap := map[string]interface{}{
|
||||
"versions": versionsToDelete,
|
||||
}
|
||||
_, err := kv.c.Logical().WriteWithContext(ctx, pathToDelete, versionsMap)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting secret at %s: %w", pathToDelete, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteMetadata deletes all versions and metadata of the secret at the
|
||||
// given path.
|
||||
func (kv *KVv2) DeleteMetadata(ctx context.Context, secretPath string) error {
|
||||
pathToDelete := fmt.Sprintf("%s/metadata/%s", kv.mountPath, secretPath)
|
||||
|
||||
_, err := kv.c.Logical().DeleteWithContext(ctx, pathToDelete)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting secret metadata at %s: %w", pathToDelete, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Undelete undeletes the given versions of a secret, restoring the data
|
||||
// so that it can be fetched again with Get requests.
|
||||
//
|
||||
// A list of existing versions can be retrieved using the GetVersionsAsList method.
|
||||
func (kv *KVv2) Undelete(ctx context.Context, secretPath string, versions []int) error {
|
||||
pathToUndelete := fmt.Sprintf("%s/undelete/%s", kv.mountPath, secretPath)
|
||||
|
||||
data := map[string]interface{}{
|
||||
"versions": versions,
|
||||
}
|
||||
|
||||
_, err := kv.c.Logical().WriteWithContext(ctx, pathToUndelete, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error undeleting secret metadata at %s: %w", pathToUndelete, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Destroy permanently removes the specified secret versions' data
|
||||
// from the Vault server. If no secret exists at the given path, no
|
||||
// action will be taken.
|
||||
//
|
||||
// A list of existing versions can be retrieved using the GetVersionsAsList method.
|
||||
func (kv *KVv2) Destroy(ctx context.Context, secretPath string, versions []int) error {
|
||||
pathToDestroy := fmt.Sprintf("%s/destroy/%s", kv.mountPath, secretPath)
|
||||
|
||||
data := map[string]interface{}{
|
||||
"versions": versions,
|
||||
}
|
||||
|
||||
_, err := kv.c.Logical().WriteWithContext(ctx, pathToDestroy, data)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error destroying secret metadata at %s: %w", pathToDestroy, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rollback can be used to roll a secret back to a previous
|
||||
// non-deleted/non-destroyed version. That previous version becomes the
|
||||
// next/newest version for the path.
|
||||
func (kv *KVv2) Rollback(ctx context.Context, secretPath string, toVersion int) (*KVSecret, error) {
|
||||
// First, do a read to get the current version for check-and-set
|
||||
latest, err := kv.Get(ctx, secretPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get latest version of secret: %w", err)
|
||||
}
|
||||
|
||||
// Make sure a value already exists
|
||||
if latest == nil {
|
||||
return nil, fmt.Errorf("no secret was found: %w", err)
|
||||
}
|
||||
|
||||
// Verify metadata found
|
||||
if latest.VersionMetadata == nil {
|
||||
return nil, fmt.Errorf("no metadata found; rollback can only be used on existing data")
|
||||
}
|
||||
|
||||
// Now run it again and read the version we want to roll back to
|
||||
rollbackVersion, err := kv.GetVersion(ctx, secretPath, toVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get previous version %d of secret: %s", toVersion, err)
|
||||
}
|
||||
|
||||
err = validateRollbackVersion(rollbackVersion)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid rollback version %d: %w", toVersion, err)
|
||||
}
|
||||
|
||||
casVersion := latest.VersionMetadata.Version
|
||||
kvs, err := kv.Put(ctx, secretPath, rollbackVersion.Data, WithCheckAndSet(casVersion))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to roll back to previous secret version: %w", err)
|
||||
}
|
||||
|
||||
return kvs, nil
|
||||
}
|
||||
|
||||
func extractCustomMetadata(secret *Secret) (map[string]interface{}, error) {
|
||||
// Logical Writes return the metadata directly, Reads return it nested inside the "metadata" key
|
||||
customMetadataInterface, ok := secret.Data["custom_metadata"]
|
||||
if !ok {
|
||||
metadataInterface, ok := secret.Data["metadata"]
|
||||
if !ok { // if that's not found, bail since it should have had one or the other
|
||||
return nil, fmt.Errorf("secret is missing expected fields")
|
||||
}
|
||||
metadataMap, ok := metadataInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected type for 'metadata' element: %T (%#v)", metadataInterface, metadataInterface)
|
||||
}
|
||||
customMetadataInterface, ok = metadataMap["custom_metadata"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("metadata missing expected field \"custom_metadata\": %v", metadataMap)
|
||||
}
|
||||
}
|
||||
|
||||
cm, ok := customMetadataInterface.(map[string]interface{})
|
||||
if !ok && customMetadataInterface != nil {
|
||||
return nil, fmt.Errorf("unexpected type for 'metadata' element: %T (%#v)", customMetadataInterface, customMetadataInterface)
|
||||
}
|
||||
|
||||
return cm, nil
|
||||
}
|
||||
|
||||
func extractDataAndVersionMetadata(secret *Secret) (*KVSecret, error) {
|
||||
// A nil map is a valid value for data: secret.Data will be nil when this
|
||||
// version of the secret has been deleted, but the metadata is still
|
||||
// available.
|
||||
var data map[string]interface{}
|
||||
if secret.Data != nil {
|
||||
dataInterface, ok := secret.Data["data"]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("missing expected 'data' element")
|
||||
}
|
||||
|
||||
if dataInterface != nil {
|
||||
data, ok = dataInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected type for 'data' element: %T (%#v)", data, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
metadata, err := extractVersionMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get version metadata: %w", err)
|
||||
}
|
||||
|
||||
return &KVSecret{
|
||||
Data: data,
|
||||
VersionMetadata: metadata,
|
||||
Raw: secret,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func extractVersionMetadata(secret *Secret) (*KVVersionMetadata, error) {
|
||||
var metadata *KVVersionMetadata
|
||||
|
||||
if secret.Data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Logical Writes return the metadata directly, Reads return it nested inside the "metadata" key
|
||||
var metadataMap map[string]interface{}
|
||||
metadataInterface, ok := secret.Data["metadata"]
|
||||
if ok {
|
||||
metadataMap, ok = metadataInterface.(map[string]interface{})
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("unexpected type for 'metadata' element: %T (%#v)", metadataInterface, metadataInterface)
|
||||
}
|
||||
} else {
|
||||
metadataMap = secret.Data
|
||||
}
|
||||
|
||||
// deletion_time usually comes in as an empty string which can't be
|
||||
// processed as time.RFC3339, so we reset it to a convertible value
|
||||
if metadataMap["deletion_time"] == "" {
|
||||
metadataMap["deletion_time"] = time.Time{}
|
||||
}
|
||||
|
||||
d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
Result: &metadata,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up decoder for API response: %w", err)
|
||||
}
|
||||
|
||||
err = d.Decode(metadataMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding metadata from API response into VersionMetadata: %w", err)
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func extractFullMetadata(secret *Secret) (*KVMetadata, error) {
|
||||
var metadata *KVMetadata
|
||||
|
||||
if secret.Data == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
if versions, ok := secret.Data["versions"]; ok {
|
||||
versionsMap := versions.(map[string]interface{})
|
||||
if len(versionsMap) > 0 {
|
||||
for version, metadata := range versionsMap {
|
||||
metadataMap := metadata.(map[string]interface{})
|
||||
// deletion_time usually comes in as an empty string which can't be
|
||||
// processed as time.RFC3339, so we reset it to a convertible value
|
||||
if metadataMap["deletion_time"] == "" {
|
||||
metadataMap["deletion_time"] = time.Time{}
|
||||
}
|
||||
versionInt, err := strconv.Atoi(version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error converting version %s to integer: %w", version, err)
|
||||
}
|
||||
metadataMap["version"] = versionInt
|
||||
versionsMap[version] = metadataMap // save the updated copy of the metadata map
|
||||
}
|
||||
}
|
||||
secret.Data["versions"] = versionsMap // save the updated copy of the versions map
|
||||
}
|
||||
|
||||
d, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
||||
mapstructure.StringToTimeHookFunc(time.RFC3339),
|
||||
mapstructure.StringToTimeDurationHookFunc(),
|
||||
),
|
||||
Result: &metadata,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error setting up decoder for API response: %w", err)
|
||||
}
|
||||
|
||||
err = d.Decode(secret.Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error decoding metadata from API response into KVMetadata: %w", err)
|
||||
}
|
||||
|
||||
return metadata, nil
|
||||
}
|
||||
|
||||
func validateRollbackVersion(rollbackVersion *KVSecret) error {
|
||||
// Make sure a value already exists
|
||||
if rollbackVersion == nil || rollbackVersion.Data == nil {
|
||||
return fmt.Errorf("no secret found")
|
||||
}
|
||||
|
||||
// Verify metadata found
|
||||
if rollbackVersion.VersionMetadata == nil {
|
||||
return fmt.Errorf("no version metadata found; rollback only works on existing data")
|
||||
}
|
||||
|
||||
// Verify it hasn't been deleted
|
||||
if !rollbackVersion.VersionMetadata.DeletionTime.IsZero() {
|
||||
return fmt.Errorf("cannot roll back to a version that has been deleted")
|
||||
}
|
||||
|
||||
if rollbackVersion.VersionMetadata.Destroyed {
|
||||
return fmt.Errorf("cannot roll back to a version that has been destroyed")
|
||||
}
|
||||
|
||||
// Verify old data found
|
||||
if rollbackVersion.Data == nil {
|
||||
return fmt.Errorf("no data found; rollback only works on existing data")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func mergePatch(ctx context.Context, client *Client, mountPath string, secretPath string, newData map[string]interface{}, opts ...KVOption) (*KVSecret, error) {
|
||||
pathToMergePatch := fmt.Sprintf("%s/data/%s", mountPath, secretPath)
|
||||
|
||||
// take any other additional options provided
|
||||
// and pass them along to the patch request
|
||||
wrappedData := map[string]interface{}{
|
||||
"data": newData,
|
||||
}
|
||||
options := make(map[string]interface{})
|
||||
for _, opt := range opts {
|
||||
k, v := opt()
|
||||
options[k] = v
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
wrappedData["options"] = options
|
||||
}
|
||||
|
||||
secret, err := client.Logical().JSONMergePatch(ctx, pathToMergePatch, wrappedData)
|
||||
if err != nil {
|
||||
// If it's a 405, that probably means the server is running a pre-1.9
|
||||
// Vault version that doesn't support the HTTP PATCH method.
|
||||
// Fall back to the old way of doing it.
|
||||
if re, ok := err.(*ResponseError); ok && re.StatusCode == 405 {
|
||||
return readThenWrite(ctx, client, mountPath, secretPath, newData)
|
||||
}
|
||||
|
||||
if re, ok := err.(*ResponseError); ok && re.StatusCode == 403 {
|
||||
return nil, fmt.Errorf("received 403 from Vault server; please ensure that token's policy has \"patch\" capability: %w", err)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("error performing merge patch to %s: %s", pathToMergePatch, err)
|
||||
}
|
||||
|
||||
metadata, err := extractVersionMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("secret was written successfully, but unable to view version metadata from response: %w", err)
|
||||
}
|
||||
|
||||
kvSecret := &KVSecret{
|
||||
Data: nil, // secret.Data in this case is the metadata
|
||||
VersionMetadata: metadata,
|
||||
Raw: secret,
|
||||
}
|
||||
|
||||
cm, err := extractCustomMetadata(secret)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading custom metadata for secret %s: %w", secretPath, err)
|
||||
}
|
||||
kvSecret.CustomMetadata = cm
|
||||
|
||||
return kvSecret, nil
|
||||
}
|
||||
|
||||
func readThenWrite(ctx context.Context, client *Client, mountPath string, secretPath string, newData map[string]interface{}) (*KVSecret, error) {
|
||||
// First, read the secret.
|
||||
existingVersion, err := client.KVv2(mountPath).Get(ctx, secretPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading secret as part of read-then-write patch operation: %w", err)
|
||||
}
|
||||
|
||||
// Make sure the secret already exists
|
||||
if existingVersion == nil || existingVersion.Data == nil {
|
||||
return nil, fmt.Errorf("no existing secret was found at %s when doing read-then-write patch operation: %w", secretPath, err)
|
||||
}
|
||||
|
||||
// Verify existing secret has metadata
|
||||
if existingVersion.VersionMetadata == nil {
|
||||
return nil, fmt.Errorf("no metadata found at %s; patch can only be used on existing data", secretPath)
|
||||
}
|
||||
|
||||
// Copy new data over with existing data
|
||||
combinedData := existingVersion.Data
|
||||
for k, v := range newData {
|
||||
combinedData[k] = v
|
||||
}
|
||||
|
||||
updatedSecret, err := client.KVv2(mountPath).Put(ctx, secretPath, combinedData, WithCheckAndSet(existingVersion.VersionMetadata.Version))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error writing secret to %s: %w", secretPath, err)
|
||||
}
|
||||
|
||||
return updatedSecret, nil
|
||||
}
|
||||
|
||||
func toMetadataMap(patchInput KVMetadataPatchInput) (map[string]interface{}, error) {
|
||||
metadataMap := make(map[string]interface{})
|
||||
|
||||
const (
|
||||
casRequiredKey = "cas_required"
|
||||
deleteVersionAfterKey = "delete_version_after"
|
||||
maxVersionsKey = "max_versions"
|
||||
customMetadataKey = "custom_metadata"
|
||||
)
|
||||
|
||||
// The KVMetadataPatchInput struct is designed to have pointer fields so that
|
||||
// the user can easily express the difference between explicitly setting a
|
||||
// field back to its zero value (e.g. false), as opposed to just having
|
||||
// the field remain unchanged (e.g. nil). This way, they only need to pass
|
||||
// the fields they want to change.
|
||||
if patchInput.MaxVersions != nil {
|
||||
metadataMap[maxVersionsKey] = *(patchInput.MaxVersions)
|
||||
}
|
||||
if patchInput.CASRequired != nil {
|
||||
metadataMap[casRequiredKey] = *(patchInput.CASRequired)
|
||||
}
|
||||
if patchInput.CustomMetadata != nil {
|
||||
if len(patchInput.CustomMetadata) == 0 { // empty non-nil map means delete all the keys
|
||||
metadataMap[customMetadataKey] = nil
|
||||
} else {
|
||||
metadataMap[customMetadataKey] = patchInput.CustomMetadata
|
||||
}
|
||||
}
|
||||
if patchInput.DeleteVersionAfter != nil {
|
||||
metadataMap[deleteVersionAfterKey] = patchInput.DeleteVersionAfter.String()
|
||||
}
|
||||
|
||||
return metadataMap, nil
|
||||
}
|
22
vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go
generated
vendored
22
vendor/github.com/hashicorp/vault/sdk/helper/certutil/helpers.go
generated
vendored
@ -446,18 +446,30 @@ func ParsePublicKeyPEM(data []byte) (interface{}, error) {
|
||||
return nil, errors.New("data does not contain any valid public keys")
|
||||
}
|
||||
|
||||
// addPolicyIdentifiers adds certificate policies extension
|
||||
//
|
||||
// AddPolicyIdentifiers adds certificate policies extension, based on CreationBundle
|
||||
func AddPolicyIdentifiers(data *CreationBundle, certTemplate *x509.Certificate) {
|
||||
for _, oidstr := range data.Params.PolicyIdentifiers {
|
||||
oid, err := StringToOid(oidstr)
|
||||
oidOnly := true
|
||||
for _, oidStr := range data.Params.PolicyIdentifiers {
|
||||
oid, err := StringToOid(oidStr)
|
||||
if err == nil {
|
||||
certTemplate.PolicyIdentifiers = append(certTemplate.PolicyIdentifiers, oid)
|
||||
}
|
||||
if err != nil {
|
||||
oidOnly = false
|
||||
}
|
||||
}
|
||||
if !oidOnly { // Because all policy information is held in the same extension, when we use an extra extension to
|
||||
// add policy qualifier information, that overwrites any information in the PolicyIdentifiers field on the Cert
|
||||
// Template, so we need to reparse all the policy identifiers here
|
||||
extension, err := CreatePolicyInformationExtensionFromStorageStrings(data.Params.PolicyIdentifiers)
|
||||
if err == nil {
|
||||
// If this errors out, don't add it, rely on the OIDs parsed into PolicyIdentifiers above
|
||||
certTemplate.ExtraExtensions = append(certTemplate.ExtraExtensions, *extension)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// addExtKeyUsageOids adds custom extended key usage OIDs to certificate
|
||||
// AddExtKeyUsageOids adds custom extended key usage OIDs to certificate
|
||||
func AddExtKeyUsageOids(data *CreationBundle, certTemplate *x509.Certificate) {
|
||||
for _, oidstr := range data.Params.ExtKeyUsageOIDs {
|
||||
oid, err := StringToOid(oidstr)
|
||||
|
114
vendor/github.com/hashicorp/vault/sdk/helper/certutil/types.go
generated
vendored
114
vendor/github.com/hashicorp/vault/sdk/helper/certutil/types.go
generated
vendored
@ -17,7 +17,10 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"encoding/asn1"
|
||||
"encoding/json"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net"
|
||||
@ -894,3 +897,114 @@ func (p *KeyBundle) ToPrivateKeyPemString() (string, error) {
|
||||
|
||||
return "", errutil.InternalError{Err: "No Private Key Bytes to Wrap"}
|
||||
}
|
||||
|
||||
// PolicyIdentifierWithQualifierEntry Structure for Internal Storage
|
||||
type PolicyIdentifierWithQualifierEntry struct {
|
||||
PolicyIdentifierOid string `json:"oid",mapstructure:"oid"`
|
||||
CPS string `json:"cps,omitempty",mapstructure:"cps"`
|
||||
Notice string `json:"notice,omitempty",mapstructure:"notice"`
|
||||
}
|
||||
|
||||
// GetPolicyIdentifierFromString parses out the internal structure of a Policy Identifier
|
||||
func GetPolicyIdentifierFromString(policyIdentifier string) (*PolicyIdentifierWithQualifierEntry, error) {
|
||||
if policyIdentifier == "" {
|
||||
return nil, nil
|
||||
}
|
||||
entry := &PolicyIdentifierWithQualifierEntry{}
|
||||
// Either a OID, or a JSON Entry: First check OID:
|
||||
_, err := StringToOid(policyIdentifier)
|
||||
if err == nil {
|
||||
entry.PolicyIdentifierOid = policyIdentifier
|
||||
return entry, nil
|
||||
}
|
||||
// Now Check If JSON Entry
|
||||
jsonErr := json.Unmarshal([]byte(policyIdentifier), &entry)
|
||||
if jsonErr != nil { // Neither, if we got here
|
||||
return entry, errors.New(fmt.Sprintf("Policy Identifier %q is neither a valid OID: %s, Nor JSON Policy Identifier: %s", policyIdentifier, err.Error(), jsonErr.Error()))
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
|
||||
// Policy Identifier with Qualifier Structure for ASN Marshalling:
|
||||
|
||||
var policyInformationOid = asn1.ObjectIdentifier{2, 5, 29, 32}
|
||||
|
||||
type policyInformation struct {
|
||||
PolicyIdentifier asn1.ObjectIdentifier
|
||||
Qualifiers []interface{} `asn1:"tag:optional,omitempty"`
|
||||
}
|
||||
|
||||
var cpsPolicyQualifierID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 1}
|
||||
|
||||
type cpsUrlPolicyQualifier struct {
|
||||
PolicyQualifierID asn1.ObjectIdentifier
|
||||
Qualifier string `asn1:"tag:optional,ia5"`
|
||||
}
|
||||
|
||||
var userNoticePolicyQualifierID = asn1.ObjectIdentifier{1, 3, 6, 1, 5, 5, 7, 2, 2}
|
||||
|
||||
type userNoticePolicyQualifier struct {
|
||||
PolicyQualifierID asn1.ObjectIdentifier
|
||||
Qualifier userNotice
|
||||
}
|
||||
|
||||
type userNotice struct {
|
||||
ExplicitText string `asn1:"tag:optional,utf8"`
|
||||
}
|
||||
|
||||
func createPolicyIdentifierWithQualifier(entry PolicyIdentifierWithQualifierEntry) (*policyInformation, error) {
|
||||
// Each Policy is Identified by a Unique ID, as designated here:
|
||||
policyOid, err := StringToOid(entry.PolicyIdentifierOid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pi := policyInformation{
|
||||
PolicyIdentifier: policyOid,
|
||||
}
|
||||
if entry.CPS != "" {
|
||||
qualifier := cpsUrlPolicyQualifier{
|
||||
PolicyQualifierID: cpsPolicyQualifierID,
|
||||
Qualifier: entry.CPS,
|
||||
}
|
||||
pi.Qualifiers = append(pi.Qualifiers, qualifier)
|
||||
}
|
||||
if entry.Notice != "" {
|
||||
qualifier := userNoticePolicyQualifier{
|
||||
PolicyQualifierID: userNoticePolicyQualifierID,
|
||||
Qualifier: userNotice{
|
||||
ExplicitText: entry.Notice,
|
||||
},
|
||||
}
|
||||
pi.Qualifiers = append(pi.Qualifiers, qualifier)
|
||||
}
|
||||
return &pi, nil
|
||||
}
|
||||
|
||||
// CreatePolicyInformationExtensionFromStorageStrings parses the stored policyIdentifiers, which might be JSON Policy
|
||||
// Identifier with Qualifier Entries or String OIDs, and returns an extension if everything parsed correctly, and an
|
||||
// error if constructing
|
||||
func CreatePolicyInformationExtensionFromStorageStrings(policyIdentifiers []string) (*pkix.Extension, error) {
|
||||
var policyInformationList []policyInformation
|
||||
for _, policyIdentifierStr := range policyIdentifiers {
|
||||
policyIdentifierEntry, err := GetPolicyIdentifierFromString(policyIdentifierStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policyIdentifierEntry != nil { // Okay to skip empty entries if there is no error
|
||||
policyInformationStruct, err := createPolicyIdentifierWithQualifier(*policyIdentifierEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
policyInformationList = append(policyInformationList, *policyInformationStruct)
|
||||
}
|
||||
}
|
||||
asn1Bytes, err := asn1.Marshal(policyInformationList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &pkix.Extension{
|
||||
Id: policyInformationOid,
|
||||
Critical: false,
|
||||
Value: asn1Bytes,
|
||||
}, nil
|
||||
}
|
||||
|
2
vendor/github.com/hashicorp/vault/sdk/version/version_base.go
generated
vendored
2
vendor/github.com/hashicorp/vault/sdk/version/version_base.go
generated
vendored
@ -11,7 +11,7 @@ var (
|
||||
// Whether cgo is enabled or not; set at build time
|
||||
CgoEnabled bool
|
||||
|
||||
Version = "1.11.0"
|
||||
Version = "1.12.0"
|
||||
VersionPrerelease = "dev1"
|
||||
VersionMetadata = ""
|
||||
)
|
||||
|
10
vendor/modules.txt
vendored
10
vendor/modules.txt
vendored
@ -271,7 +271,7 @@ github.com/hashicorp/go-rootcerts
|
||||
# github.com/hashicorp/go-secure-stdlib/mlock v0.1.1
|
||||
## explicit; go 1.16
|
||||
github.com/hashicorp/go-secure-stdlib/mlock
|
||||
# github.com/hashicorp/go-secure-stdlib/parseutil v0.1.5
|
||||
# github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6
|
||||
## explicit; go 1.16
|
||||
github.com/hashicorp/go-secure-stdlib/parseutil
|
||||
# github.com/hashicorp/go-secure-stdlib/strutil v0.1.2
|
||||
@ -305,11 +305,11 @@ github.com/hashicorp/hcl/json/token
|
||||
## explicit; go 1.13
|
||||
github.com/hashicorp/vault/command/agent/auth
|
||||
github.com/hashicorp/vault/command/agent/auth/kubernetes
|
||||
# github.com/hashicorp/vault/api v1.6.0
|
||||
## explicit; go 1.13
|
||||
# github.com/hashicorp/vault/api v1.7.2
|
||||
## explicit; go 1.17
|
||||
github.com/hashicorp/vault/api
|
||||
# github.com/hashicorp/vault/sdk v0.5.0
|
||||
## explicit; go 1.16
|
||||
# github.com/hashicorp/vault/sdk v0.5.1
|
||||
## explicit; go 1.17
|
||||
github.com/hashicorp/vault/sdk/helper/certutil
|
||||
github.com/hashicorp/vault/sdk/helper/compressutil
|
||||
github.com/hashicorp/vault/sdk/helper/consts
|
||||
|
Loading…
Reference in New Issue
Block a user