1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-15 17:51:01 +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
l.AppRolePath = "myapprole"
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 {
return err
}
@ -265,7 +265,7 @@ func (l *Vault) configureVault() error {
// parse role id
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 {
return err
}

View file

@ -65,22 +65,22 @@ func newVaultProvider(f *framework.Framework) *vaultProvider {
func (s *vaultProvider) CreateSecret(key string, val framework.SecretEntry) {
req := s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret/data/%s", key))
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())
req = s.client.NewRequest(http.MethodPost, fmt.Sprintf("/v1/secret_v1/%s", key))
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())
}
func (s *vaultProvider) DeleteSecret(key string) {
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())
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())
}

5
go.mod
View file

@ -57,7 +57,10 @@ require (
github.com/google/go-cmp v0.5.7
github.com/google/uuid v1.3.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/lestrrat-go/jwx v1.2.22
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/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
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.4.1/go.mod h1:LkMdrZnWNrFaQyYYazWVn7KshilfDidgVBq6YiTq/bM=
github.com/hashicorp/vault/api v1.3.0/go.mod h1:EabNQLI0VWbWoGlA+oBLC8PXmR9D60aUVgQGvangFWQ=
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/go.mod h1:aZ3fNuL5VNydQk8GcLJ2TV8YCRVvyaakYkhZRoVuhj0=
github.com/hashicorp/yamux v0.0.0-20180604194846-3520598351bb/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=

View file

@ -16,15 +16,62 @@ package fake
import (
"context"
"net/url"
"strings"
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)
@ -36,59 +83,11 @@ type MockSetNamespaceFn func(namespace 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 {
Metadata *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 {
return func(v string) {
for _, fn := range ofn {
@ -116,8 +115,9 @@ func NewAddHeaderFn() MockAddHeaderFn {
}
type VaultClient struct {
MockNewRequest MockNewRequestFn
MockRawRequestWithContext MockRawRequestWithContextFn
MockLogical Logical
MockAuth Auth
MockAuthToken Token
MockSetToken MockSetTokenFn
MockToken MockTokenFn
MockClearToken MockClearTokenFn
@ -125,12 +125,38 @@ type VaultClient struct {
MockAddHeader MockAddHeaderFn
}
func (c *VaultClient) NewRequest(method, requestPath string) *vault.Request {
return c.MockNewRequest(method, requestPath)
func (c *VaultClient) Logical() Logical {
return c.MockLogical
}
func (c *VaultClient) RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error) {
return c.MockRawRequestWithContext(ctx, r)
func NewVaultLogical() Logical {
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) {

View file

@ -28,6 +28,9 @@ import (
"github.com/go-logr/logr"
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"
authenticationv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1"
@ -65,9 +68,7 @@ const (
errSecretFormat = "secret data not in expected format"
errUnexpectedKey = "unexpected key in data: %s"
errVaultToken = "cannot parse Vault authentication token: %w"
errVaultReqParams = "cannot set Vault request parameters: %w"
errVaultRequest = "error from Vault request: %w"
errVaultResponse = "cannot parse Vault response: %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"
errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
@ -107,22 +108,84 @@ const (
var _ esv1beta1.SecretsClient = &client{}
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 {
NewRequest(method, requestPath string) *vault.Request
RawRequestWithContext(ctx context.Context, r *vault.Request) (*vault.Response, error)
SetToken(v string)
Token() string
ClearToken()
Auth() Auth
Logical() Logical
AuthToken() Token
SetNamespace(namespace 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 {
kube kclient.Client
corev1 typedcorev1.CoreV1Interface
store *esv1beta1.VaultProvider
log logr.Logger
corev1 typedcorev1.CoreV1Interface
client Client
auth Auth
logical Logical
token Token
namespace string
storeKind string
}
@ -136,7 +199,24 @@ func init() {
}
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 {
@ -191,13 +271,15 @@ func (c *connector) newClient(ctx context.Context, store esv1beta1.GenericStore,
if vaultSpec.ReadYourWrites && vaultSpec.ForwardInconsistent {
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
}
vStore.client = client
return vStore, nil
}
@ -340,16 +422,10 @@ func (v *client) listSecrets(ctx context.Context, path string) ([]string, error)
if err != nil {
return nil, err
}
r := v.client.NewRequest(http.MethodGet, url)
r.Params.Set("list", "true")
resp, err := v.client.RawRequestWithContext(ctx, r)
secret, err := v.logical.ListWithContext(ctx, url)
if err != nil {
return nil, fmt.Errorf(errReadSecret, err)
}
secret, parseErr := vault.ParseSecret(resp.Body)
if parseErr != nil {
return nil, parseErr
}
t, ok := secret.Data["keys"]
if !ok {
return nil, nil
@ -381,15 +457,10 @@ func (v *client) readSecretMetadata(ctx context.Context, path string) (map[strin
if err != nil {
return nil, err
}
r := v.client.NewRequest(http.MethodGet, url)
resp, err := v.client.RawRequestWithContext(ctx, r)
secret, err := v.logical.ReadWithDataWithContext(ctx, url, nil)
if err != nil {
return nil, fmt.Errorf(errReadSecret, err)
}
secret, parseErr := vault.ParseSecret(resp.Body)
if parseErr != nil {
return nil, parseErr
}
t, ok := secret.Data["custom_metadata"]
if !ok {
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 {
// 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 {
req := v.client.NewRequest(http.MethodPost, "/v1/auth/token/revoke-self")
_, err := v.client.RawRequestWithContext(ctx, req)
revoke, err := checkToken(ctx, v)
if err != nil {
return fmt.Errorf(errVaultRevokeToken, err)
}
if revoke {
err = v.token.RevokeSelfWithContext(ctx, v.client.Token())
if err != nil {
return fmt.Errorf(errVaultRevokeToken, err)
}
v.client.ClearToken()
}
}
return nil
}
func (v *client) Validate() error {
err := checkToken(context.Background(), v)
_, err := checkToken(context.Background(), v)
if err != nil {
return fmt.Errorf(errInvalidCredentials, err)
}
@ -515,9 +591,9 @@ func (v *client) buildMetadataPath(path string) (string, error) {
}
if v.store.Path == nil {
path = strings.Replace(path, "data", "metadata", 1)
url = fmt.Sprintf("/v1/%s", path)
url = path
} 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
}
@ -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
// 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
req := v.client.NewRequest(http.MethodGet, fmt.Sprintf("/v1/%s", dataPath))
var params map[string][]string
if version != "" {
req.Params.Set("version", version)
params = make(map[string][]string)
params["version"] = []string{version}
}
resp, err := v.client.RawRequestWithContext(ctx, req)
vaultSecret, err := v.logical.ReadWithDataWithContext(ctx, dataPath, params)
if err != nil {
return nil, fmt.Errorf(errReadSecret, err)
}
vaultSecret, err := vault.ParseSecret(resp.Body)
if err != nil {
return nil, err
}
secretData := vaultSecret.Data
if v.store.Version == esv1beta1.VaultKVStoreV2 {
// Vault KV2 has data embedded within sub-field
@ -686,33 +756,33 @@ func getCertFromConfigMap(v *client) ([]byte, error) {
return []byte(val), nil
}
func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
tokenExists, err := setSecretKeyToken(ctx, v, client)
func (v *client) setAuth(ctx context.Context, cfg *vault.Config) error {
tokenExists, err := setSecretKeyToken(ctx, v)
if tokenExists {
return err
}
tokenExists, err = setAppRoleToken(ctx, v, client)
tokenExists, err = setAppRoleToken(ctx, v)
if tokenExists {
return err
}
tokenExists, err = setKubernetesAuthToken(ctx, v, client)
tokenExists, err = setKubernetesAuthToken(ctx, v)
if tokenExists {
return err
}
tokenExists, err = setLdapAuthToken(ctx, v, client)
tokenExists, err = setLdapAuthToken(ctx, v)
if tokenExists {
return err
}
tokenExists, err = setJwtAuthToken(ctx, v, client)
tokenExists, err = setJwtAuthToken(ctx, v)
if tokenExists {
return err
}
tokenExists, err = setCertAuthToken(ctx, v, client, cfg)
tokenExists, err = setCertAuthToken(ctx, v, cfg)
if tokenExists {
return err
}
@ -720,79 +790,74 @@ func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config)
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
if tokenRef != nil {
token, err := v.secretKeyRef(ctx, tokenRef)
if err != nil {
return true, err
}
client.SetToken(token)
v.client.SetToken(token)
return true, 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
if appRole != nil {
token, err := v.requestTokenWithAppRoleRef(ctx, client, appRole)
err := v.requestTokenWithAppRoleRef(ctx, appRole)
if err != nil {
return true, err
}
client.SetToken(token)
return true, 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
if kubernetesAuth != nil {
token, err := v.requestTokenWithKubernetesAuth(ctx, client, kubernetesAuth)
err := v.requestTokenWithKubernetesAuth(ctx, kubernetesAuth)
if err != nil {
return true, err
}
client.SetToken(token)
return true, 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
if ldapAuth != nil {
token, err := v.requestTokenWithLdapAuth(ctx, client, ldapAuth)
err := v.requestTokenWithLdapAuth(ctx, ldapAuth)
if err != nil {
return true, err
}
client.SetToken(token)
return true, 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
if jwtAuth != nil {
token, err := v.requestTokenWithJwtAuth(ctx, client, jwtAuth)
err := v.requestTokenWithJwtAuth(ctx, jwtAuth)
if err != nil {
return true, err
}
client.SetToken(token)
return true, 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
if certAuth != nil {
token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
err := v.requestTokenWithCertAuth(ctx, certAuth, cfg)
if err != nil {
return true, err
}
client.SetToken(token)
return true, 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.
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
req := vStore.client.NewRequest("GET", "/v1/auth/token/lookup-self")
_, err := vStore.client.RawRequestWithContext(ctx, req)
return 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,
resp, err := vStore.token.LookupSelfWithContext(ctx)
if err != nil {
return false, err
}
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)
secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef)
if err != nil {
return "", err
return err
}
parameters := appRoleParameters(roleID, secretID)
url := strings.Join([]string{"/v1", "auth", appRole.Path, "login"}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
secret := approle.SecretID{FromString: secretID}
appRoleClient, err := approle.NewAppRoleAuth(roleID, &secret, approle.WithMountPath(appRole.Path))
if err != nil {
return "", fmt.Errorf(errVaultReqParams, err)
return err
}
resp, err := client.RawRequestWithContext(ctx, request)
_, err = v.auth.Login(ctx, appRoleClient)
if err != nil {
return "", fmt.Errorf(errVaultRequest, err)
return err
}
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
return nil
}
// kubeParameters creates the required body for Vault Kubernetes auth.
// 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) {
func (v *client) requestTokenWithKubernetesAuth(ctx context.Context, kubernetesAuth *esv1beta1.VaultKubernetesAuth) error {
jwtString, err := getJwtString(ctx, v, kubernetesAuth)
if err != nil {
return "", err
return err
}
parameters := kubeParameters(kubernetesAuth.Role, jwtString)
url := strings.Join([]string{"/v1", "auth", kubernetesAuth.Path, "login"}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
k, err := authkubernetes.NewKubernetesAuth(kubernetesAuth.Role, authkubernetes.WithServiceAccountToken(jwtString), authkubernetes.WithMountPath(kubernetesAuth.Path))
if err != nil {
return "", fmt.Errorf(errVaultReqParams, err)
return err
}
resp, err := client.RawRequestWithContext(ctx, request)
_, err = v.auth.Login(ctx, k)
if err != nil {
return "", fmt.Errorf(errVaultRequest, err)
return err
}
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
return nil
}
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)
password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef)
if err != nil {
return "", err
return err
}
parameters := map[string]string{
"password": password,
}
url := strings.Join([]string{"/v1", "auth", ldapAuth.Path, "login", username}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
pass := authldap.Password{FromString: password}
l, err := authldap.NewLDAPAuth(username, &pass, authldap.WithMountPath(ldapAuth.Path))
if err != nil {
return "", fmt.Errorf(errVaultReqParams, err)
return err
}
resp, err := client.RawRequestWithContext(ctx, request)
_, err = v.auth.Login(ctx, l)
if err != nil {
return "", fmt.Errorf(errVaultRequest, err)
return err
}
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
return 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)
var jwt string
var err error
if jwtAuth.SecretRef != nil {
@ -1069,80 +1068,56 @@ func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwt
err = fmt.Errorf(errJwtNoTokenSource)
}
if err != nil {
return "", err
return err
}
parameters := map[string]string{
parameters := map[string]interface{}{
"role": role,
"jwt": jwt,
}
url := strings.Join([]string{"/v1", "auth", jwtAuth.Path, "login"}, "/")
request := client.NewRequest("POST", url)
err = request.SetJSONBody(parameters)
url := strings.Join([]string{"auth", jwtAuth.Path, "login"}, "/")
vaultResult, err := v.logical.WriteWithContext(ctx, url, parameters)
if err != nil {
return "", fmt.Errorf(errVaultReqParams, 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)
return err
}
token, err := vaultResult.TokenID()
if err != nil {
return "", fmt.Errorf(errVaultToken, err)
return fmt.Errorf(errVaultToken, err)
}
return token, nil
v.client.SetToken(token)
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)
if err != nil {
return "", err
return err
}
clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert)
if err != nil {
return "", err
return err
}
cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
if err != nil {
return "", fmt.Errorf(errClientTLSAuth, err)
return fmt.Errorf(errClientTLSAuth, err)
}
if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
}
url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/")
request := client.NewRequest("POST", url)
resp, err := client.RawRequestWithContext(ctx, request)
url := strings.Join([]string{"auth", "cert", "login"}, "/")
vaultResult, err := v.logical.WriteWithContext(ctx, url, 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()
if err != nil {
return "", fmt.Errorf(errVaultToken, err)
return fmt.Errorf(errVaultToken, err)
}
return token, nil
v.client.SetToken(token)
return nil
}

View file

@ -15,13 +15,10 @@ limitations under the License.
package vault
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strings"
"testing"
"github.com/crossplane/crossplane-runtime/pkg/test"
@ -171,45 +168,6 @@ func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1beta1.SecretStore {
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 {
newClientFunc func(c *vault.Config) (Client, error)
store esv1beta1.GenericStore
@ -228,12 +186,25 @@ type testCase struct {
}
func clientWithLoginMock(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 { return nil }),
cl := fake.VaultClient{
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 {
@ -327,36 +298,6 @@ MIICsTCCAZkCFEJJ4daz5sxkFlzq9n1djLEuG7bmMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZh
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": {
reason: "Should return a Vault provider successfully",
args: args{
@ -594,7 +535,7 @@ func TestGetSecret(t *testing.T) {
type args struct {
store *esv1beta1.VaultProvider
kube kclient.Client
vClient Client
vLogical Logical
ns string
data esv1beta1.ExternalSecretDataRemoteRef
}
@ -616,11 +557,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "access_key",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secret), nil,
),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
},
},
want: want{
@ -635,11 +573,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "access_key",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNilVal), nil,
),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNilVal, nil),
},
},
want: want{
@ -652,11 +587,8 @@ func TestGetSecret(t *testing.T) {
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
data: esv1beta1.ExternalSecretDataRemoteRef{},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secret), nil,
),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
},
},
want: want{
@ -671,11 +603,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nested.foo",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNestedVal), nil,
),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNestedVal, nil),
},
},
want: want{
@ -691,11 +620,8 @@ func TestGetSecret(t *testing.T) {
//
Property: "nested.bar",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNestedVal), nil,
),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNestedVal, nil),
},
},
want: want{
@ -710,11 +636,8 @@ func TestGetSecret(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nop.doesnt.exist",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNestedVal), nil,
),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNestedVal, nil),
},
},
want: want{
@ -725,9 +648,8 @@ func TestGetSecret(t *testing.T) {
reason: "Should return error if vault client fails to read secret.",
args: args{
store: makeSecretStore().Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(nil, errBoom),
vLogical: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errBoom),
},
},
want: want{
@ -740,7 +662,7 @@ func TestGetSecret(t *testing.T) {
t.Run(name, func(t *testing.T) {
vStore := &client{
kube: tc.args.kube,
client: tc.args.vClient,
logical: tc.args.vLogical,
store: tc.args.store,
namespace: tc.args.ns,
}
@ -788,7 +710,7 @@ func TestGetSecretMap(t *testing.T) {
type args struct {
store *esv1beta1.VaultProvider
kube kclient.Client
vClient Client
vClient Logical
ns string
data esv1beta1.ExternalSecretDataRemoteRef
}
@ -807,11 +729,8 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secret), nil,
),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secret, nil),
},
},
want: want{
@ -826,15 +745,10 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(
map[string]interface{}{
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
"data": secret,
},
), nil,
),
}, nil),
},
},
want: want{
@ -849,11 +763,8 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(secretWithNilVal), nil,
),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(secretWithNilVal, nil),
},
},
want: want{
@ -869,15 +780,9 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has a nil value",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithNilVal,
},
), nil,
),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
"data": secretWithNilVal}, nil),
},
},
want: want{
@ -893,15 +798,9 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should map the secret even if it has other types",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithTypes,
},
), nil,
),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
"data": secretWithTypes}, nil),
},
},
want: want{
@ -923,15 +822,9 @@ func TestGetSecretMap(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nested",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithNestedVal,
},
), nil,
),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
"data": secretWithNestedVal}, nil),
},
},
want: want{
@ -948,15 +841,9 @@ func TestGetSecretMap(t *testing.T) {
data: esv1beta1.ExternalSecretDataRemoteRef{
Property: "nested.foo",
},
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
newVaultResponseWithData(
map[string]interface{}{
"data": secretWithNestedVal,
},
), nil,
),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(map[string]interface{}{
"data": secretWithNestedVal}, nil),
},
},
want: want{
@ -971,9 +858,8 @@ func TestGetSecretMap(t *testing.T) {
reason: "Should return error if vault client fails to read secret.",
args: args{
store: makeSecretStore().Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(nil, errBoom),
vClient: &fake.Logical{
ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errBoom),
},
},
want: want{
@ -986,7 +872,7 @@ func TestGetSecretMap(t *testing.T) {
t.Run(name, func(t *testing.T) {
vStore := &client{
kube: tc.args.kube,
client: tc.args.vClient,
logical: tc.args.vClient,
store: tc.args.store,
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) {
secret1Bytes := []byte("{\"access_key\":\"access_key\",\"access_secret\":\"access_secret\"}")
secret2Bytes := []byte("{\"access_key\":\"access_key2\",\"access_secret\":\"access_secret2\"}")
@ -1010,66 +940,66 @@ func TestGetAllSecrets(t *testing.T) {
path := "path"
secret := map[string]interface{}{
"secret1": map[string]interface{}{
"data": map[string]interface{}{
"access_key": "access_key",
"access_secret": "access_secret",
},
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "bar",
},
},
"data": map[string]interface{}{
"access_key": "access_key",
"access_secret": "access_secret",
},
},
"secret2": map[string]interface{}{
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "baz",
},
},
"data": map[string]interface{}{
"access_key": "access_key2",
"access_secret": "access_secret2",
},
},
"tag": map[string]interface{}{
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "baz",
},
},
},
"tag": map[string]interface{}{
"data": map[string]interface{}{
"access_key": "unfetched",
"access_secret": "unfetched",
},
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "baz",
},
},
},
"path/1": map[string]interface{}{
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "path",
},
},
"data": map[string]interface{}{
"access_key": "path1",
"access_secret": "path1",
},
},
"path/2": map[string]interface{}{
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "path",
},
},
},
"path/2": map[string]interface{}{
"data": map[string]interface{}{
"access_key": "path2",
"access_secret": "path2",
},
"metadata": map[string]interface{}{
"custom_metadata": map[string]interface{}{
"foo": "path",
},
},
},
"default": map[string]interface{}{
"data": map[string]interface{}{
"empty": "true",
},
"metadata": map[string]interface{}{
"keys": []string{"secret1", "secret2", "tag", "path/"},
"keys": []interface{}{"secret1", "secret2", "tag", "path/"},
},
},
"path/": map[string]interface{}{
@ -1077,14 +1007,14 @@ func TestGetAllSecrets(t *testing.T) {
"empty": "true",
},
"metadata": map[string]interface{}{
"keys": []string{"1", "2"},
"keys": []interface{}{"1", "2"},
},
},
}
type args struct {
store *esv1beta1.VaultProvider
kube kclient.Client
vClient Client
vLogical Logical
ns string
data esv1beta1.ExternalSecretFind
}
@ -1103,11 +1033,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should map multiple secrets matching name",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn(
newVaultResponseWithMetadata(secret), nil,
),
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
},
data: esv1beta1.ExternalSecretFind{
Name: &esv1beta1.FindName{
@ -1127,11 +1055,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should map multiple secrets matching tags",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn(
newVaultResponseWithMetadata(secret), nil,
),
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
},
data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{
@ -1151,11 +1077,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should filter secrets based on path",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn(
newVaultResponseWithMetadata(secret), nil,
),
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
},
data: esv1beta1.ExternalSecretFind{
Path: &path,
@ -1176,11 +1100,9 @@ func TestGetAllSecrets(t *testing.T) {
reason: "should not work if using kv1 store",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vClient: &fake.VaultClient{
MockNewRequest: fake.NewMockNewRequestListFn(&vault.Request{}),
MockRawRequestWithContext: fake.NewMockRawRequestListWithContextFn(
newVaultResponseWithMetadata(secret), nil,
),
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
},
data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{
@ -1198,7 +1120,7 @@ func TestGetAllSecrets(t *testing.T) {
t.Run(name, func(t *testing.T) {
vStore := &client{
kube: tc.args.kube,
client: tc.args.vClient,
logical: tc.args.vLogical,
store: tc.args.store,
namespace: tc.args.ns,
}