1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

Merge pull request #959 from external-secrets/chore/refactor-vault

Chore/refactor vault
This commit is contained in:
paul-the-alien[bot] 2022-04-13 13:02:14 +00:00 committed by GitHub
commit 84af221762
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 437 additions and 502 deletions

View file

@ -251,7 +251,7 @@ func (l *Vault) configureVault() error {
// configure appRole // configure appRole
l.AppRolePath = "myapprole" l.AppRolePath = "myapprole"
req := l.VaultClient.NewRequest(http.MethodGet, fmt.Sprintf("/v1/auth/%s/role/eso-e2e-role/role-id", l.AppRolePath)) req := l.VaultClient.NewRequest(http.MethodGet, fmt.Sprintf("/v1/auth/%s/role/eso-e2e-role/role-id", l.AppRolePath))
res, err := l.VaultClient.RawRequest(req) res, err := l.VaultClient.RawRequest(req) //nolint:staticcheck
if err != nil { if err != nil {
return err return err
} }
@ -265,7 +265,7 @@ func (l *Vault) configureVault() error {
// parse role id // parse role id
req = l.VaultClient.NewRequest(http.MethodPost, fmt.Sprintf("/v1/auth/%s/role/eso-e2e-role/secret-id", l.AppRolePath)) req = l.VaultClient.NewRequest(http.MethodPost, fmt.Sprintf("/v1/auth/%s/role/eso-e2e-role/secret-id", l.AppRolePath))
res, err = l.VaultClient.RawRequest(req) res, err = l.VaultClient.RawRequest(req) //nolint:staticcheck
if err != nil { if err != nil {
return err return err
} }

View file

@ -65,22 +65,22 @@ func newVaultProvider(f *framework.Framework) *vaultProvider {
func (s *vaultProvider) CreateSecret(key string, val framework.SecretEntry) { func (s *vaultProvider) CreateSecret(key string, val framework.SecretEntry) {
req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key)) req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key))
req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val.Value)) req.BodyBytes = []byte(fmt.Sprintf(`{"data": %s}`, val.Value))
_, err := s.client.RawRequestWithContext(context.Background(), req) _, err := s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
req = s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret_v1/%s", key)) req = s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret_v1/%s", key))
req.BodyBytes = []byte(val.Value) req.BodyBytes = []byte(val.Value)
_, err = s.client.RawRequestWithContext(context.Background(), req) _, err = s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }
func (s *vaultProvider) DeleteSecret(key string) { func (s *vaultProvider) DeleteSecret(key string) {
req := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret/data/%s", key)) req := s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret/data/%s", key))
_, err := s.client.RawRequestWithContext(context.Background(), req) _, err := s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
req = s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret_v1/%s", key)) req = s.client.NewRequest(http.MethodDelete, fmt.Sprintf("/v1/secret_v1/%s", key))
_, err = s.client.RawRequestWithContext(context.Background(), req) _, err = s.client.RawRequestWithContext(context.Background(), req) //nolint:staticcheck
Expect(err).ToNot(HaveOccurred()) Expect(err).ToNot(HaveOccurred())
} }

5
go.mod
View file

@ -57,7 +57,10 @@ require (
github.com/google/go-cmp v0.5.7 github.com/google/go-cmp v0.5.7
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/googleapis/gax-go/v2 v2.2.0 github.com/googleapis/gax-go/v2 v2.2.0
github.com/hashicorp/vault/api v1.4.1 github.com/hashicorp/vault/api v1.5.0
github.com/hashicorp/vault/api/auth/approle v0.1.1
github.com/hashicorp/vault/api/auth/kubernetes v0.1.0
github.com/hashicorp/vault/api/auth/ldap v0.1.0
github.com/huandu/xstrings v1.3.2 // indirect github.com/huandu/xstrings v1.3.2 // indirect
github.com/lestrrat-go/jwx v1.2.22 github.com/lestrrat-go/jwx v1.2.22
github.com/onsi/ginkgo/v2 v2.1.3 github.com/onsi/ginkgo/v2 v2.1.3

13
go.sum
View file

@ -489,8 +489,17 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/vault/api v1.4.1 h1:mWLfPT0RhxBitjKr6swieCEP2v5pp/M//t70S3kMLRo= github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ=
github.com/hashicorp/vault/api v1.4.1/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM= github.com/hashicorp/vault/api v1.3.1/go.mod h1:QeJoWxMFt+MsuWcYhmwRLwKEXrjwAFFywzhptMsTIUw=
github.com/hashicorp/vault/api v1.5.0 h1:Bp6yc2bn7CWkOrVIzFT/Qurzx528bdavF3nz590eu28=
github.com/hashicorp/vault/api v1.5.0/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/api/auth/approle v0.1.1 h1:R5yA+xcNvw1ix6bDuWOaLOq2L4L77zDCVsethNw97xQ=
github.com/hashicorp/vault/api/auth/approle v0.1.1/go.mod h1:mHOLgh//xDx4dpqXoq6tS8Ob0FoCFWLU2ibJ26Lfmag=
github.com/hashicorp/vault/api/auth/kubernetes v0.1.0 h1:6BtyahbF4aQp8gg3ww0A/oIoqzbhpNP1spXU3nHE0n0=
github.com/hashicorp/vault/api/auth/kubernetes v0.1.0/go.mod h1:Pdgk78uIs0mgDOLvc3a+h/vYIT9rznw2sz+ucuH9024=
github.com/hashicorp/vault/api/auth/ldap v0.1.0 h1:runn+BIRU6/QcGirhstoJIqO+plVuTN/zf401tbB5H0=
github.com/hashicorp/vault/api/auth/ldap v0.1.0/go.mod h1:vl3YZyt+bRtTHvVqKWeOTCI5I40t31t0S48efipZq64=
github.com/hashicorp/vault/sdk v0.3.0/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo= github.com/hashicorp/vault/sdk v0.4.1 h1:3SaHOJY687jY1fnB61PtL0cOkKItphrbLmux7T92HBo=
github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0= github.com/hashicorp/vault/sdk v0.4.1/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=

View file

@ -16,15 +16,62 @@ package fake
import ( import (
"context" "context"
"net/url"
"strings"
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
) )
type MockNewRequestFn func(method, requestPath string) *vault.Request type LoginFn func(ctx context.Context, authMethod vault.AuthMethod) (*vault.Secret, error)
type Auth struct {
LoginFn LoginFn
}
type MockRawRequestWithContextFn func(ctx context.Context, r *vault.Request) (*vault.Response, error) func (f Auth) Login(ctx context.Context, authMethod vault.AuthMethod) (*vault.Secret, error) {
return f.LoginFn(ctx, authMethod)
}
type ReadWithDataWithContextFn func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error)
type ListWithContextFn func(ctx context.Context, path string) (*vault.Secret, error)
type WriteWithContextFn func(ctx context.Context, path string, data map[string]interface{}) (*vault.Secret, error)
type Logical struct {
ReadWithDataWithContextFn ReadWithDataWithContextFn
ListWithContextFn ListWithContextFn
WriteWithContextFn WriteWithContextFn
}
func NewReadWithContextFn(secret map[string]interface{}, err error) ReadWithDataWithContextFn {
return func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
vault := &vault.Secret{
Data: secret,
}
return vault, err
}
}
func (f Logical) ReadWithDataWithContext(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
return f.ReadWithDataWithContextFn(ctx, path, data)
}
func (f Logical) ListWithContext(ctx context.Context, path string) (*vault.Secret, error) {
return f.ListWithContextFn(ctx, path)
}
func (f Logical) WriteWithContext(ctx context.Context, path string, data map[string]interface{}) (*vault.Secret, error) {
return f.WriteWithContextFn(ctx, path, data)
}
type RevokeSelfWithContextFn func(ctx context.Context, token string) error
type LookupSelfWithContextFn func(ctx context.Context) (*vault.Secret, error)
type Token struct {
RevokeSelfWithContextFn RevokeSelfWithContextFn
LookupSelfWithContextFn LookupSelfWithContextFn
}
func (f Token) RevokeSelfWithContext(ctx context.Context, token string) error {
return f.RevokeSelfWithContextFn(ctx, token)
}
func (f Token) LookupSelfWithContext(ctx context.Context) (*vault.Secret, error) {
return f.LookupSelfWithContextFn(ctx)
}
type MockSetTokenFn func(v string) type MockSetTokenFn func(v string)
@ -36,59 +83,11 @@ type MockSetNamespaceFn func(namespace string)
type MockAddHeaderFn func(key, value string) type MockAddHeaderFn func(key, value string)
func NewMockNewRequestFn(req *vault.Request) MockNewRequestFn {
return func(method, requestPath string) *vault.Request {
return req
}
}
func NewMockNewRequestListFn(req *vault.Request) MockNewRequestFn {
return func(method, requestPath string) *vault.Request {
urlPath := url.URL{
Path: requestPath,
}
req.URL = &urlPath
req.Params = make(url.Values)
return req
}
}
// An RequestFn operates on the supplied Request. You might use an RequestFn to
// test or update the contents of an Request.
type RequestFn func(req *vault.Request) error
func NewMockRawRequestWithContextFn(res *vault.Response, err error, ofn ...RequestFn) MockRawRequestWithContextFn {
return func(_ context.Context, r *vault.Request) (*vault.Response, error) {
for _, fn := range ofn {
if err := fn(r); err != nil {
return res, err
}
}
return res, err
}
}
type VaultListResponse struct { type VaultListResponse struct {
Metadata *vault.Response Metadata *vault.Response
Data *vault.Response Data *vault.Response
} }
func NewMockRawRequestListWithContextFn(res map[string]VaultListResponse, err error) MockRawRequestWithContextFn {
return func(_ context.Context, r *vault.Request) (*vault.Response, error) {
pathList := strings.Split(r.URL.Path, "/")
path := "default"
if pathList[4] != "" {
path = strings.Join(pathList[4:], "/")
}
if strings.Contains(r.URL.Path, "metadata") {
resp := res[path].Metadata
return resp, err
}
resp := res[path].Data
return resp, err
}
}
func NewSetTokenFn(ofn ...func(v string)) MockSetTokenFn { func NewSetTokenFn(ofn ...func(v string)) MockSetTokenFn {
return func(v string) { return func(v string) {
for _, fn := range ofn { for _, fn := range ofn {
@ -116,21 +115,48 @@ func NewAddHeaderFn() MockAddHeaderFn {
} }
type VaultClient struct { type VaultClient struct {
MockNewRequest MockNewRequestFn MockLogical Logical
MockRawRequestWithContext MockRawRequestWithContextFn MockAuth Auth
MockSetToken MockSetTokenFn MockAuthToken Token
MockToken MockTokenFn MockSetToken MockSetTokenFn
MockClearToken MockClearTokenFn MockToken MockTokenFn
MockSetNamespace MockSetNamespaceFn MockClearToken MockClearTokenFn
MockAddHeader MockAddHeaderFn MockSetNamespace MockSetNamespaceFn
MockAddHeader MockAddHeaderFn
} }
func (c *VaultClient) NewRequest(method, requestPath string) *vault.Request { func (c *VaultClient) Logical() Logical {
return c.MockNewRequest(method, requestPath) return c.MockLogical
} }
func (c *VaultClient) RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error) { func NewVaultLogical() Logical {
return c.MockRawRequestWithContext(ctx, r) logical := Logical{
ReadWithDataWithContextFn: func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
return nil, nil
},
ListWithContextFn: func(ctx context.Context, path string) (*vault.Secret, error) {
return nil, nil
},
WriteWithContextFn: func(ctx context.Context, path string, data map[string]interface{}) (*vault.Secret, error) {
return nil, nil
},
}
return logical
}
func (c *VaultClient) Auth() Auth {
return c.MockAuth
}
func NewVaultAuth() Auth {
auth := Auth{
LoginFn: func(ctx context.Context, authMethod vault.AuthMethod) (*vault.Secret, error) {
return nil, nil
},
}
return auth
}
func (c *VaultClient) AuthToken() Token {
return c.MockAuthToken
} }
func (c *VaultClient) SetToken(v string) { func (c *VaultClient) SetToken(v string) {

View file

@ -28,6 +28,9 @@ import (
"github.com/go-logr/logr" "github.com/go-logr/logr"
vault "github.com/hashicorp/vault/api" vault "github.com/hashicorp/vault/api"
approle "github.com/hashicorp/vault/api/auth/approle"
authkubernetes "github.com/hashicorp/vault/api/auth/kubernetes"
authldap "github.com/hashicorp/vault/api/auth/ldap"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
authenticationv1 "k8s.io/api/authentication/v1" authenticationv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
@ -65,9 +68,7 @@ const (
errSecretFormat = "secret data not in expected format" errSecretFormat = "secret data not in expected format"
errUnexpectedKey = "unexpected key in data: %s" errUnexpectedKey = "unexpected key in data: %s"
errVaultToken = "cannot parse Vault authentication token: %w" errVaultToken = "cannot parse Vault authentication token: %w"
errVaultReqParams = "cannot set Vault request parameters: %w"
errVaultRequest = "error from Vault request: %w" errVaultRequest = "error from Vault request: %w"
errVaultResponse = "cannot parse Vault response: %w"
errServiceAccount = "cannot read Kubernetes service account token from file system: %w" errServiceAccount = "cannot read Kubernetes service account token from file system: %w"
errJwtNoTokenSource = "neither `secretRef` nor `kubernetesServiceAccountToken` was supplied as token source for jwt authentication" errJwtNoTokenSource = "neither `secretRef` nor `kubernetesServiceAccountToken` was supplied as token source for jwt authentication"
errUnsupportedKvVersion = "cannot perform find operations with kv version v1" errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
@ -107,22 +108,84 @@ const (
var _ esv1beta1.SecretsClient = &client{} var _ esv1beta1.SecretsClient = &client{}
var _ esv1beta1.Provider = &connector{} var _ esv1beta1.Provider = &connector{}
type Auth interface {
Login(ctx context.Context, authMethod vault.AuthMethod) (*vault.Secret, error)
}
type Token interface {
RevokeSelfWithContext(ctx context.Context, token string) error
LookupSelfWithContext(ctx context.Context) (*vault.Secret, error)
}
type Logical interface {
ReadWithDataWithContext(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error)
ListWithContext(ctx context.Context, path string) (*vault.Secret, error)
WriteWithContext(ctx context.Context, path string, data map[string]interface{}) (*vault.Secret, error)
}
type Client interface { type Client interface {
NewRequest(method, requestPath string) *vault.Request
RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error)
SetToken(v string) SetToken(v string)
Token() string Token() string
ClearToken() ClearToken()
Auth() Auth
Logical() Logical
AuthToken() Token
SetNamespace(namespace string) SetNamespace(namespace string)
AddHeader(key, value string) AddHeader(key, value string)
} }
type VClient struct {
setToken func(v string)
token func() string
clearToken func()
auth Auth
logical Logical
authToken Token
setNamespace func(namespace string)
addHeader func(key, value string)
}
func (v VClient) AddHeader(key, value string) {
v.addHeader(key, value)
}
func (v VClient) SetNamespace(namespace string) {
v.setNamespace(namespace)
}
func (v VClient) ClearToken() {
v.clearToken()
}
func (v VClient) Token() string {
return v.token()
}
func (v VClient) SetToken(token string) {
v.setToken(token)
}
func (v VClient) Auth() Auth {
return v.auth
}
func (v VClient) AuthToken() Token {
return v.authToken
}
func (v VClient) Logical() Logical {
return v.logical
}
type client struct { type client struct {
kube kclient.Client kube kclient.Client
corev1 typedcorev1.CoreV1Interface
store *esv1beta1.VaultProvider store *esv1beta1.VaultProvider
log logr.Logger log logr.Logger
corev1 typedcorev1.CoreV1Interface
client Client client Client
auth Auth
logical Logical
token Token
namespace string namespace string
storeKind string storeKind string
} }
@ -136,7 +199,24 @@ func init() {
} }
func newVaultClient(c *vault.Config) (Client, error) { func newVaultClient(c *vault.Config) (Client, error) {
return vault.NewClient(c) cl, err := vault.NewClient(c)
if err != nil {
return nil, err
}
auth := cl.Auth()
logical := cl.Logical()
token := cl.Auth().Token()
out := VClient{
setToken: cl.SetToken,
token: cl.Token,
clearToken: cl.ClearToken,
auth: auth,
authToken: token,
logical: logical,
setNamespace: cl.SetNamespace,
addHeader: cl.AddHeader,
}
return out, nil
} }
type connector struct { type connector struct {
@ -191,13 +271,15 @@ func (c *connector) newClient(ctx context.Context, store esv1beta1.GenericStore,
if vaultSpec.ReadYourWrites && vaultSpec.ForwardInconsistent { if vaultSpec.ReadYourWrites && vaultSpec.ForwardInconsistent {
client.AddHeader("X-Vault-Inconsistent", "forward-active-node") client.AddHeader("X-Vault-Inconsistent", "forward-active-node")
} }
vStore.client = client
vStore.auth = client.Auth()
vStore.logical = client.Logical()
vStore.token = client.AuthToken()
if err := vStore.setAuth(ctx, client, cfg); err != nil { if err := vStore.setAuth(ctx, cfg); err != nil {
return nil, err return nil, err
} }
vStore.client = client
return vStore, nil return vStore, nil
} }
@ -340,16 +422,10 @@ func (v *client) listSecrets(ctx context.Context, path string) ([]string, error)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r := v.client.NewRequest(http.MethodGet, url) secret, err := v.logical.ListWithContext(ctx, url)
r.Params.Set("list", "true")
resp, err := v.client.RawRequestWithContext(ctx, r)
if err != nil { if err != nil {
return nil, fmt.Errorf(errReadSecret, err) return nil, fmt.Errorf(errReadSecret, err)
} }
secret, parseErr := vault.ParseSecret(resp.Body)
if parseErr != nil {
return nil, parseErr
}
t, ok := secret.Data["keys"] t, ok := secret.Data["keys"]
if !ok { if !ok {
return nil, nil return nil, nil
@ -381,15 +457,10 @@ func (v *client) readSecretMetadata(ctx context.Context, path string) (map[strin
if err != nil { if err != nil {
return nil, err return nil, err
} }
r := v.client.NewRequest(http.MethodGet, url) secret, err := v.logical.ReadWithDataWithContext(ctx, url, nil)
resp, err := v.client.RawRequestWithContext(ctx, r)
if err != nil { if err != nil {
return nil, fmt.Errorf(errReadSecret, err) return nil, fmt.Errorf(errReadSecret, err)
} }
secret, parseErr := vault.ParseSecret(resp.Body)
if parseErr != nil {
return nil, parseErr
}
t, ok := secret.Data["custom_metadata"] t, ok := secret.Data["custom_metadata"]
if !ok { if !ok {
return nil, nil return nil, nil
@ -490,18 +561,23 @@ func getTypedKey(data map[string]interface{}, key string) ([]byte, error) {
func (v *client) Close(ctx context.Context) error { func (v *client) Close(ctx context.Context) error {
// Revoke the token if we have one set and it wasn't sourced from a TokenSecretRef // Revoke the token if we have one set and it wasn't sourced from a TokenSecretRef
if v.client.Token() != "" && v.store.Auth.TokenSecretRef == nil { if v.client.Token() != "" && v.store.Auth.TokenSecretRef == nil {
req := v.client.NewRequest(http.MethodPost, "/v1/auth/token/revoke-self") revoke, err := checkToken(ctx, v)
_, err := v.client.RawRequestWithContext(ctx, req)
if err != nil { if err != nil {
return fmt.Errorf(errVaultRevokeToken, err) return fmt.Errorf(errVaultRevokeToken, err)
} }
v.client.ClearToken() if revoke {
err = v.token.RevokeSelfWithContext(ctx, v.client.Token())
if err != nil {
return fmt.Errorf(errVaultRevokeToken, err)
}
v.client.ClearToken()
}
} }
return nil return nil
} }
func (v *client) Validate() error { func (v *client) Validate() error {
err := checkToken(context.Background(), v) _, err := checkToken(context.Background(), v)
if err != nil { if err != nil {
return fmt.Errorf(errInvalidCredentials, err) return fmt.Errorf(errInvalidCredentials, err)
} }
@ -515,9 +591,9 @@ func (v *client) buildMetadataPath(path string) (string, error) {
} }
if v.store.Path == nil { if v.store.Path == nil {
path = strings.Replace(path, "data", "metadata", 1) path = strings.Replace(path, "data", "metadata", 1)
url = fmt.Sprintf("/v1/%s", path) url = path
} else { } else {
url = fmt.Sprintf("/v1/%s/metadata/%s", *v.store.Path, path) url = fmt.Sprintf("%s/metadata/%s", *v.store.Path, path)
} }
return url, nil return url, nil
} }
@ -554,21 +630,15 @@ func (v *client) readSecret(ctx context.Context, path, version string) (map[stri
// path formated according to vault docs for v1 and v2 API // path formated according to vault docs for v1 and v2 API
// v1: https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret // v1: https://www.vaultproject.io/api-docs/secret/kv/kv-v1#read-secret
// v2: https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version // v2: https://www.vaultproject.io/api/secret/kv/kv-v2#read-secret-version
req := v.client.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s", dataPath)) var params map[string][]string
if version != "" { if version != "" {
req.Params.Set("version", version) params = make(map[string][]string)
params["version"] = []string{version}
} }
vaultSecret, err := v.logical.ReadWithDataWithContext(ctx, dataPath, params)
resp, err := v.client.RawRequestWithContext(ctx, req)
if err != nil { if err != nil {
return nil, fmt.Errorf(errReadSecret, err) return nil, fmt.Errorf(errReadSecret, err)
} }
vaultSecret, err := vault.ParseSecret(resp.Body)
if err != nil {
return nil, err
}
secretData := vaultSecret.Data secretData := vaultSecret.Data
if v.store.Version == esv1beta1.VaultKVStoreV2 { if v.store.Version == esv1beta1.VaultKVStoreV2 {
// Vault KV2 has data embedded within sub-field // Vault KV2 has data embedded within sub-field
@ -686,33 +756,33 @@ func getCertFromConfigMap(v *client) ([]byte, error) {
return []byte(val), nil return []byte(val), nil
} }
func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error { func (v *client) setAuth(ctx context.Context, cfg *vault.Config) error {
tokenExists, err := setSecretKeyToken(ctx, v, client) tokenExists, err := setSecretKeyToken(ctx, v)
if tokenExists { if tokenExists {
return err return err
} }
tokenExists, err = setAppRoleToken(ctx, v, client) tokenExists, err = setAppRoleToken(ctx, v)
if tokenExists { if tokenExists {
return err return err
} }
tokenExists, err = setKubernetesAuthToken(ctx, v, client) tokenExists, err = setKubernetesAuthToken(ctx, v)
if tokenExists { if tokenExists {
return err return err
} }
tokenExists, err = setLdapAuthToken(ctx, v, client) tokenExists, err = setLdapAuthToken(ctx, v)
if tokenExists { if tokenExists {
return err return err
} }
tokenExists, err = setJwtAuthToken(ctx, v, client) tokenExists, err = setJwtAuthToken(ctx, v)
if tokenExists { if tokenExists {
return err return err
} }
tokenExists, err = setCertAuthToken(ctx, v, client, cfg) tokenExists, err = setCertAuthToken(ctx, v, cfg)
if tokenExists { if tokenExists {
return err return err
} }
@ -720,79 +790,74 @@ func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config)
return errors.New(errAuthFormat) return errors.New(errAuthFormat)
} }
func setAppRoleToken(ctx context.Context, v *client, client Client) (bool, error) { func setAppRoleToken(ctx context.Context, v *client) (bool, error) {
tokenRef := v.store.Auth.TokenSecretRef tokenRef := v.store.Auth.TokenSecretRef
if tokenRef != nil { if tokenRef != nil {
token, err := v.secretKeyRef(ctx, tokenRef) token, err := v.secretKeyRef(ctx, tokenRef)
if err != nil { if err != nil {
return true, err return true, err
} }
client.SetToken(token) v.client.SetToken(token)
return true, nil return true, nil
} }
return false, nil return false, nil
} }
func setSecretKeyToken(ctx context.Context, v *client, client Client) (bool, error) { func setSecretKeyToken(ctx context.Context, v *client) (bool, error) {
appRole := v.store.Auth.AppRole appRole := v.store.Auth.AppRole
if appRole != nil { if appRole != nil {
token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole) err := v.requestTokenWithAppRoleRef(ctx, appRole)
if err != nil { if err != nil {
return true, err return true, err
} }
client.SetToken(token)
return true, nil return true, nil
} }
return false, nil return false, nil
} }
func setKubernetesAuthToken(ctx context.Context, v *client, client Client) (bool, error) { func setKubernetesAuthToken(ctx context.Context, v *client) (bool, error) {
kubernetesAuth := v.store.Auth.Kubernetes kubernetesAuth := v.store.Auth.Kubernetes
if kubernetesAuth != nil { if kubernetesAuth != nil {
token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth) err := v.requestTokenWithKubernetesAuth(ctx, kubernetesAuth)
if err != nil { if err != nil {
return true, err return true, err
} }
client.SetToken(token)
return true, nil return true, nil
} }
return false, nil return false, nil
} }
func setLdapAuthToken(ctx context.Context, v *client, client Client) (bool, error) { func setLdapAuthToken(ctx context.Context, v *client) (bool, error) {
ldapAuth := v.store.Auth.Ldap ldapAuth := v.store.Auth.Ldap
if ldapAuth != nil { if ldapAuth != nil {
token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth) err := v.requestTokenWithLdapAuth(ctx, ldapAuth)
if err != nil { if err != nil {
return true, err return true, err
} }
client.SetToken(token)
return true, nil return true, nil
} }
return false, nil return false, nil
} }
func setJwtAuthToken(ctx context.Context, v *client, client Client) (bool, error) { func setJwtAuthToken(ctx context.Context, v *client) (bool, error) {
jwtAuth := v.store.Auth.Jwt jwtAuth := v.store.Auth.Jwt
if jwtAuth != nil { if jwtAuth != nil {
token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth) err := v.requestTokenWithJwtAuth(ctx, jwtAuth)
if err != nil { if err != nil {
return true, err return true, err
} }
client.SetToken(token)
return true, nil return true, nil
} }
return false, nil return false, nil
} }
func setCertAuthToken(ctx context.Context, v *client, client Client, cfg *vault.Config) (bool, error) { func setCertAuthToken(ctx context.Context, v *client, cfg *vault.Config) (bool, error) {
certAuth := v.store.Auth.Cert certAuth := v.store.Auth.Cert
if certAuth != nil { if certAuth != nil {
token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg) err := v.requestTokenWithCertAuth(ctx, certAuth, cfg)
if err != nil { if err != nil {
return true, err return true, err
} }
client.SetToken(token)
return true, nil return true, nil
} }
return false, nil return false, nil
@ -878,101 +943,56 @@ func (v *client) serviceAccountToken(ctx context.Context, serviceAccountRef esme
} }
// checkToken does a lookup and checks if the provided token exists. // checkToken does a lookup and checks if the provided token exists.
func checkToken(ctx context.Context, vStore *client) error { func checkToken(ctx context.Context, vStore *client) (bool, error) {
// https://www.vaultproject.io/api-docs/auth/token#lookup-a-token-self // https://www.vaultproject.io/api-docs/auth/token#lookup-a-token-self
req := vStore.client.NewRequest("GET", "/v1/auth/token/lookup-self") resp, err := vStore.token.LookupSelfWithContext(ctx)
_, err := vStore.client.RawRequestWithContext(ctx, req) if err != nil {
return err return false, err
}
// appRoleParameters creates the required body for Vault AppRole Auth.
// Reference - https://www.vaultproject.io/api-docs/auth/approle#login-with-approle
func appRoleParameters(role, secret string) map[string]string {
return map[string]string{
"role_id": role,
"secret_id": secret,
} }
t, ok := resp.Data["type"]
if !ok {
return false, fmt.Errorf("could not assert token type")
}
tokenType := t.(string)
if tokenType == "batch" {
return false, nil
}
return true, nil
} }
func (v *client) requestTokenWithAppRoleRef(ctx context.Context, client Client, appRole *esv1beta1.VaultAppRole) (string, error) { func (v *client) requestTokenWithAppRoleRef(ctx context.Context, appRole *esv1beta1.VaultAppRole) error {
roleID := strings.TrimSpace(appRole.RoleID) roleID := strings.TrimSpace(appRole.RoleID)
secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef) secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef)
if err != nil { if err != nil {
return "", err return err
} }
secret := approle.SecretID{FromString: secretID}
parameters := appRoleParameters(roleID, secretID) appRoleClient, err := approle.NewAppRoleAuth(roleID, &secret, approle.WithMountPath(appRole.Path))
url := strings.Join([]string{"/v1", "auth", appRole.Path, "login"}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultReqParams, err) return err
} }
_, err = v.auth.Login(ctx, appRoleClient)
resp, err := client.RawRequestWithContext(ctx, request)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultRequest, err) return err
} }
return nil
defer resp.Body.Close()
vaultResult := vault.Secret{}
if err = resp.DecodeJSON(&vaultResult); err != nil {
return "", fmt.Errorf(errVaultResponse, err)
}
token, err := vaultResult.TokenID()
if err != nil {
return "", fmt.Errorf(errVaultToken, err)
}
return token, nil
} }
// kubeParameters creates the required body for Vault Kubernetes auth. func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, kubernetesAuth *esv1beta1.VaultKubernetesAuth) error {
// Reference - https://www.vaultproject.io/api/auth/kubernetes#login
func kubeParameters(role, jwt string) map[string]string {
return map[string]string{
"role": role,
"jwt": jwt,
}
}
func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, client Client, kubernetesAuth *esv1beta1.VaultKubernetesAuth) (string, error) {
jwtString, err := getJwtString(ctx, v, kubernetesAuth) jwtString, err := getJwtString(ctx, v, kubernetesAuth)
if err != nil { if err != nil {
return "", err return err
} }
k, err := authkubernetes.NewKubernetesAuth(kubernetesAuth.Role, authkubernetes.WithServiceAccountToken(jwtString), authkubernetes.WithMountPath(kubernetesAuth.Path))
parameters := kubeParameters(kubernetesAuth.Role, jwtString)
url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultReqParams, err) return err
} }
_, err = v.auth.Login(ctx, k)
resp, err := client.RawRequestWithContext(ctx, request)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultRequest, err) return err
} }
return nil
defer resp.Body.Close()
vaultResult := vault.Secret{}
err = resp.DecodeJSON(&vaultResult)
if err != nil {
return "", fmt.Errorf(errVaultResponse, err)
}
token, err := vaultResult.TokenID()
if err != nil {
return "", fmt.Errorf(errVaultToken, err)
}
return token, nil
} }
func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1beta1.VaultKubernetesAuth) (string, error) { func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1beta1.VaultKubernetesAuth) (string, error) {
@ -1008,48 +1028,27 @@ func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1beta1.Vaul
} }
} }
func (v *client) requestTokenWithLdapAuth(ctx context.Context, client Client, ldapAuth *esv1beta1.VaultLdapAuth) (string, error) { func (v *client) requestTokenWithLdapAuth(ctx context.Context, ldapAuth *esv1beta1.VaultLdapAuth) error {
username := strings.TrimSpace(ldapAuth.Username) username := strings.TrimSpace(ldapAuth.Username)
password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef) password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
if err != nil { if err != nil {
return "", err return err
} }
pass := authldap.Password{FromString: password}
parameters := map[string]string{ l, err := authldap.NewLDAPAuth(username, &pass, authldap.WithMountPath(ldapAuth.Path))
"password": password,
}
url := strings.Join([]string{"/v1", "auth", ldapAuth.Path, "login", username}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultReqParams, err) return err
} }
_, err = v.auth.Login(ctx, l)
resp, err := client.RawRequestWithContext(ctx, request)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultRequest, err) return err
} }
return nil
defer resp.Body.Close()
vaultResult := vault.Secret{}
if err = resp.DecodeJSON(&vaultResult); err != nil {
return "", fmt.Errorf(errVaultResponse, err)
}
token, err := vaultResult.TokenID()
if err != nil {
return "", fmt.Errorf(errVaultToken, err)
}
return token, nil
} }
func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwtAuth *esv1beta1.VaultJwtAuth) (string, error) { func (v *client) requestTokenWithJwtAuth(ctx context.Context, jwtAuth *esv1beta1.VaultJwtAuth) error {
role := strings.TrimSpace(jwtAuth.Role) role := strings.TrimSpace(jwtAuth.Role)
var jwt string var jwt string
var err error var err error
if jwtAuth.SecretRef != nil { if jwtAuth.SecretRef != nil {
@ -1069,80 +1068,56 @@ func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwt
err = fmt.Errorf(errJwtNoTokenSource) err = fmt.Errorf(errJwtNoTokenSource)
} }
if err != nil { if err != nil {
return "", err return err
} }
parameters := map[string]string{ parameters := map[string]interface{}{
"role": role, "role": role,
"jwt": jwt, "jwt": jwt,
} }
url := strings.Join([]string{"/v1", "auth", jwtAuth.Path, "login"}, "/") url := strings.Join([]string{"auth", jwtAuth.Path, "login"}, "/")
request := client.NewRequest("POST", url) vaultResult, err := v.logical.WriteWithContext(ctx, url, parameters)
err = request.SetJSONBody(parameters)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultReqParams, err) return err
}
resp, err := client.RawRequestWithContext(ctx, request)
if err != nil {
return "", fmt.Errorf(errVaultRequest, err)
}
defer resp.Body.Close()
vaultResult := vault.Secret{}
if err = resp.DecodeJSON(&vaultResult); err != nil {
return "", fmt.Errorf(errVaultResponse, err)
} }
token, err := vaultResult.TokenID() token, err := vaultResult.TokenID()
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultToken, err) return fmt.Errorf(errVaultToken, err)
} }
v.client.SetToken(token)
return token, nil return nil
} }
func (v *client) requestTokenWithCertAuth(ctx context.Context, client Client, certAuth *esv1beta1.VaultCertAuth, cfg *vault.Config) (string, error) { func (v *client) requestTokenWithCertAuth(ctx context.Context, certAuth *esv1beta1.VaultCertAuth, cfg *vault.Config) error {
clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef) clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef)
if err != nil { if err != nil {
return "", err return err
} }
clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert) clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert)
if err != nil { if err != nil {
return "", err return err
} }
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey)) cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil { if err != nil {
return "", fmt.Errorf(errClientTLSAuth, err) return fmt.Errorf(errClientTLSAuth, err)
} }
if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok { if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
transport.TLSClientConfig.Certificates = []tls.Certificate{cert} transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
} }
url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/") url := strings.Join([]string{"auth", "cert", "login"}, "/")
request := client.NewRequest("POST", url) vaultResult, err := v.logical.WriteWithContext(ctx, url, nil)
resp, err := client.RawRequestWithContext(ctx, request)
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultRequest, err) return fmt.Errorf(errVaultRequest, err)
} }
defer resp.Body.Close()
vaultResult := vault.Secret{}
if err = resp.DecodeJSON(&vaultResult); err != nil {
return "", fmt.Errorf(errVaultResponse, err)
}
token, err := vaultResult.TokenID() token, err := vaultResult.TokenID()
if err != nil { if err != nil {
return "", fmt.Errorf(errVaultToken, err) return fmt.Errorf(errVaultToken, err)
} }
v.client.SetToken(token)
return token, nil return nil
} }

View file

@ -15,13 +15,10 @@ limitations under the License.
package vault package vault
import ( import (
"bytes"
"context" "context"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io" "strings"
"net/http"
"testing" "testing"
"github.com/crossplane/crossplane-runtime/pkg/test" "github.com/crossplane/crossplane-runtime/pkg/test"
@ -171,45 +168,6 @@ func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1beta1.SecretStore {
return store return store
} }
func newVaultResponse(data *vault.Secret) *vault.Response {
jsonData, _ := json.Marshal(data)
return &vault.Response{
Response: &http.Response{
Body: io.NopCloser(bytes.NewReader(jsonData)),
},
}
}
func newVaultResponseWithData(data map[string]interface{}) *vault.Response {
return newVaultResponse(&vault.Secret{
Data: data,
})
}
func newVaultResponseWithMetadata(content map[string]interface{}) map[string]fake.VaultListResponse {
ans := make(map[string]fake.VaultListResponse)
for k, v := range content {
t := v.(map[string]interface{})
m := t["metadata"].(map[string]interface{})
listResponse := fake.VaultListResponse{
Data: newVaultResponse(&vault.Secret{
Data: t,
}),
Metadata: newVaultResponse(&vault.Secret{
Data: m,
}),
}
ans[k] = listResponse
}
return ans
}
func newVaultTokenIDResponse(token string) *vault.Response {
return newVaultResponseWithData(map[string]interface{}{
"id": token,
})
}
type args struct { type args struct {
newClientFunc func(c *vault.Config) (Client, error) newClientFunc func(c *vault.Config) (Client, error)
store esv1beta1.GenericStore store esv1beta1.GenericStore
@ -228,12 +186,25 @@ type testCase struct {
} }
func clientWithLoginMock(c *vault.Config) (Client, error) { func clientWithLoginMock(c *vault.Config) (Client, error) {
return &fake.VaultClient{ cl := fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error { return nil }),
MockSetToken: fake.NewSetTokenFn(), MockSetToken: fake.NewSetTokenFn(),
}, nil MockAuth: fake.NewVaultAuth(),
MockLogical: fake.NewVaultLogical(),
}
auth := cl.Auth()
token := cl.AuthToken()
logical := cl.Logical()
out := VClient{
setToken: cl.SetToken,
token: cl.Token,
clearToken: cl.ClearToken,
auth: auth,
authToken: token,
logical: logical,
setNamespace: cl.SetNamespace,
addHeader: cl.AddHeader,
}
return out, nil
} }
func kubeMockWithSecretTokenAndServiceAcc(obj kclient.Object) error { func kubeMockWithSecretTokenAndServiceAcc(obj kclient.Object) error {
@ -327,36 +298,6 @@ MIICsTCCAZkCFEJJ4daz5sxkFlzq9n1djLEuG7bmMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZh
err: fmt.Errorf(errGetKubeSecret, "vault-secret", errBoom), err: fmt.Errorf(errGetKubeSecret, "vault-secret", errBoom),
}, },
}, },
"SuccessfulVaultStore": {
reason: "Should return a Vault provider successfully",
args: args{
store: makeSecretStore(),
kube: &test.MockClient{
MockGet: test.NewMockGetFn(nil, kubeMockWithSecretTokenAndServiceAcc),
},
newClientFunc: func(c *vault.Config) (Client, error) {
return &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error {
kubeRole := makeValidSecretStore().Spec.Provider.Vault.Auth.Kubernetes.Role
want := kubeParameters(kubeRole, string(secretData))
if diff := cmp.Diff(want, got.Obj); diff != "" {
t.Errorf("RawRequestWithContext(...): -want, +got:\n%s", diff)
}
return nil
}),
MockSetToken: fake.NewSetTokenFn(),
MockToken: fake.NewTokenFn(""),
MockClearToken: fake.NewClearTokenFn(),
}, nil
},
},
want: want{
err: nil,
},
},
"SuccessfulVaultStoreWithCertAuth": { "SuccessfulVaultStoreWithCertAuth": {
reason: "Should return a Vault provider successfully", reason: "Should return a Vault provider successfully",
args: args{ args: args{
@ -592,11 +533,11 @@ func TestGetSecret(t *testing.T) {
} }
type args struct { type args struct {
store *esv1beta1.VaultProvider store *esv1beta1.VaultProvider
kube kclient.Client kube kclient.Client
vClient Client vLogical Logical
ns string ns string
data esv1beta1.ExternalSecretDataRemoteRef data esv1beta1.ExternalSecretDataRemoteRef
} }
type want struct { type want struct {
@ -616,11 +557,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{ data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "access_key", Property: "access_key",
}, },
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secret), nil,
),
}, },
}, },
want: want{ want: want{
@ -635,11 +573,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{ data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "access_key", Property: "access_key",
}, },
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNilVal, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNilVal), nil,
),
}, },
}, },
want: want{ want: want{
@ -652,11 +587,8 @@ func TestGetSecret(t *testing.T) {
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
data: esv1beta1.ExternalSecretDataRemoteRef{}, data: esv1beta1.ExternalSecretDataRemoteRef{},
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secret), nil,
),
}, },
}, },
want: want{ want: want{
@ -671,11 +603,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{ data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nested.foo", Property: "nested.foo",
}, },
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNestedVal, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNestedVal), nil,
),
}, },
}, },
want: want{ want: want{
@ -691,11 +620,8 @@ func TestGetSecret(t *testing.T) {
// //
Property: "nested.bar", Property: "nested.bar",
}, },
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNestedVal, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNestedVal), nil,
),
}, },
}, },
want: want{ want: want{
@ -710,11 +636,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{ data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nop.doesnt.exist", Property: "nop.doesnt.exist",
}, },
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNestedVal, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNestedVal), nil,
),
}, },
}, },
want: want{ want: want{
@ -725,9 +648,8 @@ func TestGetSecret(t *testing.T) {
reason: "Should return error if vault client fails to read secret.", reason: "Should return error if vault client fails to read secret.",
args: args{ args: args{
store: makeSecretStore().Spec.Provider.Vault, store: makeSecretStore().Spec.Provider.Vault,
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errBoom),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(nil, errBoom),
}, },
}, },
want: want{ want: want{
@ -740,7 +662,7 @@ func TestGetSecret(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
vStore := &client{ vStore := &client{
kube: tc.args.kube, kube: tc.args.kube,
client: tc.args.vClient, logical: tc.args.vLogical,
store: tc.args.store, store: tc.args.store,
namespace: tc.args.ns, namespace: tc.args.ns,
} }
@ -788,7 +710,7 @@ func TestGetSecretMap(t *testing.T) {
type args struct { type args struct {
store *esv1beta1.VaultProvider store *esv1beta1.VaultProvider
kube kclient.Client kube kclient.Client
vClient Client vClient Logical
ns string ns string
data esv1beta1.ExternalSecretDataRemoteRef data esv1beta1.ExternalSecretDataRemoteRef
} }
@ -807,11 +729,8 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value", reason: "Should map the secret even if it has a nil value",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secret), nil,
),
}, },
}, },
want: want{ want: want{
@ -826,15 +745,10 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value", reason: "Should map the secret even if it has a nil value",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn( "data": secret,
newVaultResponseWithData( }, nil),
map[string]interface{}{
"data": secret,
},
), nil,
),
}, },
}, },
want: want{ want: want{
@ -849,11 +763,8 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value", reason: "Should map the secret even if it has a nil value",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNilVal, nil),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNilVal), nil,
),
}, },
}, },
want: want{ want: want{
@ -869,15 +780,9 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value", reason: "Should map the secret even if it has a nil value",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn( "data": secretWithNilVal}, nil),
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithNilVal,
},
), nil,
),
}, },
}, },
want: want{ want: want{
@ -893,15 +798,9 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has other types", reason: "Should map the secret even if it has other types",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn( "data": secretWithTypes}, nil),
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithTypes,
},
), nil,
),
}, },
}, },
want: want{ want: want{
@ -923,15 +822,9 @@ func TestGetSecretMap(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{ data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nested", Property: "nested",
}, },
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn( "data": secretWithNestedVal}, nil),
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithNestedVal,
},
), nil,
),
}, },
}, },
want: want{ want: want{
@ -948,15 +841,9 @@ func TestGetSecretMap(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{ data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nested.foo", Property: "nested.foo",
}, },
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn( "data": secretWithNestedVal}, nil),
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithNestedVal,
},
), nil,
),
}, },
}, },
want: want{ want: want{
@ -971,9 +858,8 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should return error if vault client fails to read secret.", reason: "Should return error if vault client fails to read secret.",
args: args{ args: args{
store: makeSecretStore().Spec.Provider.Vault, store: makeSecretStore().Spec.Provider.Vault,
vClient: &fake.VaultClient{ vClient: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}), ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errBoom),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(nil, errBoom),
}, },
}, },
want: want{ want: want{
@ -986,7 +872,7 @@ func TestGetSecretMap(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
vStore := &client{ vStore := &client{
kube: tc.args.kube, kube: tc.args.kube,
client: tc.args.vClient, logical: tc.args.vClient,
store: tc.args.store, store: tc.args.store,
namespace: tc.args.ns, namespace: tc.args.ns,
} }
@ -1001,6 +887,50 @@ func TestGetSecretMap(t *testing.T) {
} }
} }
func newListWithContextFn(secrets map[string]interface{}) func(ctx context.Context, path string) (*vault.Secret, error) {
return func(ctx context.Context, path string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/metadata/")
if path == "" {
path = "default"
}
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
}
meta := data.(map[string]interface{})
ans := meta["metadata"].(map[string]interface{})
secret := &vault.Secret{
Data: map[string]interface{}{
"keys": ans["keys"],
},
}
return secret, nil
}
}
func newReadtWithContextFn(secrets map[string]interface{}) func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
return func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/data/")
path = strings.TrimPrefix(path, "secret/metadata/")
if path == "" {
path = "default"
}
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
}
meta := data.(map[string]interface{})
metadata := meta["metadata"].(map[string]interface{})
content := map[string]interface{}{
"data": meta["data"],
"custom_metadata": metadata["custom_metadata"],
}
secret := &vault.Secret{
Data: content,
}
return secret, nil
}
}
func TestGetAllSecrets(t *testing.T) { func TestGetAllSecrets(t *testing.T) {
secret1Bytes := []byte("{\"access_key\":\"access_key\",\"access_secret\":\"access_secret\"}") secret1Bytes := []byte("{\"access_key\":\"access_key\",\"access_secret\":\"access_secret\"}")
secret2Bytes := []byte("{\"access_key\":\"access_key2\",\"access_secret\":\"access_secret2\"}") secret2Bytes := []byte("{\"access_key\":\"access_key2\",\"access_secret\":\"access_secret2\"}")
@ -1010,66 +940,66 @@ func TestGetAllSecrets(t *testing.T) {
path := "path" path := "path"
secret := map[string]interface{}{ secret := map[string]interface{}{
"secret1": map[string]interface{}{ "secret1": map[string]interface{}{
"data": map[string]interface{}{
"access_key": "access_key",
"access_secret": "access_secret",
},
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{ "custom_metadata": map[string]interface{}{
"foo": "bar", "foo": "bar",
}, },
}, },
"data": map[string]interface{}{
"access_key": "access_key",
"access_secret": "access_secret",
},
}, },
"secret2": map[string]interface{}{ "secret2": map[string]interface{}{
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "baz",
},
},
"data": map[string]interface{}{ "data": map[string]interface{}{
"access_key": "access_key2", "access_key": "access_key2",
"access_secret": "access_secret2", "access_secret": "access_secret2",
}, },
},
"tag": map[string]interface{}{
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{ "custom_metadata": map[string]interface{}{
"foo": "baz", "foo": "baz",
}, },
}, },
},
"tag": map[string]interface{}{
"data": map[string]interface{}{ "data": map[string]interface{}{
"access_key": "unfetched", "access_key": "unfetched",
"access_secret": "unfetched", "access_secret": "unfetched",
}, },
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "baz",
},
},
}, },
"path/1": map[string]interface{}{ "path/1": map[string]interface{}{
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "path",
},
},
"data": map[string]interface{}{ "data": map[string]interface{}{
"access_key": "path1", "access_key": "path1",
"access_secret": "path1", "access_secret": "path1",
}, },
},
"path/2": map[string]interface{}{
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{ "custom_metadata": map[string]interface{}{
"foo": "path", "foo": "path",
}, },
}, },
},
"path/2": map[string]interface{}{
"data": map[string]interface{}{ "data": map[string]interface{}{
"access_key": "path2", "access_key": "path2",
"access_secret": "path2", "access_secret": "path2",
}, },
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "path",
},
},
}, },
"default": map[string]interface{}{ "default": map[string]interface{}{
"data": map[string]interface{}{ "data": map[string]interface{}{
"empty": "true", "empty": "true",
}, },
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"keys": []string{"secret1", "secret2", "tag", "path/"}, "keys": []interface{}{"secret1", "secret2", "tag", "path/"},
}, },
}, },
"path/": map[string]interface{}{ "path/": map[string]interface{}{
@ -1077,16 +1007,16 @@ func TestGetAllSecrets(t *testing.T) {
"empty": "true", "empty": "true",
}, },
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"keys": []string{"1", "2"}, "keys": []interface{}{"1", "2"},
}, },
}, },
} }
type args struct { type args struct {
store *esv1beta1.VaultProvider store *esv1beta1.VaultProvider
kube kclient.Client kube kclient.Client
vClient Client vLogical Logical
ns string ns string
data esv1beta1.ExternalSecretFind data esv1beta1.ExternalSecretFind
} }
type want struct { type want struct {
@ -1103,11 +1033,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should map multiple secrets matching name", reason: "should map multiple secrets matching name",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}), ListWithContextFn: newListWithContextFn(secret),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn( ReadWithDataWithContextFn: newReadtWithContextFn(secret),
newVaultResponseWithMetadata(secret), nil,
),
}, },
data: esv1beta1.ExternalSecretFind{ data: esv1beta1.ExternalSecretFind{
Name: &esv1beta1.FindName{ Name: &esv1beta1.FindName{
@ -1127,11 +1055,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should map multiple secrets matching tags", reason: "should map multiple secrets matching tags",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}), ListWithContextFn: newListWithContextFn(secret),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn( ReadWithDataWithContextFn: newReadtWithContextFn(secret),
newVaultResponseWithMetadata(secret), nil,
),
}, },
data: esv1beta1.ExternalSecretFind{ data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{ Tags: map[string]string{
@ -1151,11 +1077,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should filter secrets based on path", reason: "should filter secrets based on path",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}), ListWithContextFn: newListWithContextFn(secret),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn( ReadWithDataWithContextFn: newReadtWithContextFn(secret),
newVaultResponseWithMetadata(secret), nil,
),
}, },
data: esv1beta1.ExternalSecretFind{ data: esv1beta1.ExternalSecretFind{
Path: &path, Path: &path,
@ -1176,11 +1100,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should not work if using kv1 store", reason: "should not work if using kv1 store",
args: args{ args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault, store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vClient: &fake.VaultClient{ vLogical: &fake.Logical{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}), ListWithContextFn: newListWithContextFn(secret),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn( ReadWithDataWithContextFn: newReadtWithContextFn(secret),
newVaultResponseWithMetadata(secret), nil,
),
}, },
data: esv1beta1.ExternalSecretFind{ data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{ Tags: map[string]string{
@ -1198,7 +1120,7 @@ func TestGetAllSecrets(t *testing.T) {
t.Run(name, func(t *testing.T) { t.Run(name, func(t *testing.T) {
vStore := &client{ vStore := &client{
kube: tc.args.kube, kube: tc.args.kube,
client: tc.args.vClient, logical: tc.args.vLogical,
store: tc.args.store, store: tc.args.store,
namespace: tc.args.ns, namespace: tc.args.ns,
} }