mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
Fix PushSecret lookup in keepersecurity provider (#4077)
* Fixed Keeper Security custom record type name in docs Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> * Fixed Keeper records lookup in PushSecret Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> * Improved Keeper record lookup to search only for records of the expected type Improved PushSecret and DeleteSecret Fixed "nil pointer dereference" errors Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> * Fixed tests Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> * chore(helm): Add extra labels to the validating webhooks (#4074) It should add a bunch of app.kubernetes.io labels Signed-off-by: Miguel Sacristán Izcue <miguel_tete17@hotmail.com> Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com> Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> * Added tests for secrets with multiple matches Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> --------- Signed-off-by: Ivan Dimov <78815270+idimov-keeper@users.noreply.github.com> Signed-off-by: Miguel Sacristán Izcue <miguel_tete17@hotmail.com> Co-authored-by: Tete17 <miguel_tete17@hotmail.com> Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
This commit is contained in:
parent
51fabd49a5
commit
b3c3e1924d
4 changed files with 87 additions and 41 deletions
|
@ -80,7 +80,7 @@ There are some limitations using this provider.
|
|||
|
||||
## Push Secrets
|
||||
|
||||
Push Secret will only work with a custom KeeperSecurity Record type `ExternalSecret`
|
||||
Push Secret will only work with a custom KeeperSecurity Record type `externalSecrets`
|
||||
|
||||
### Behavior
|
||||
* `selector`:
|
||||
|
|
|
@ -46,7 +46,7 @@ const (
|
|||
errInvalidJSONSecret = "invalid Secret. Secret %s can not be converted to JSON. %w"
|
||||
errInvalidRegex = "find.name.regex. Invalid Regular expresion %s. %w"
|
||||
errInvalidRemoteRefKey = "match.remoteRef.remoteKey. Invalid format. Format should match secretName/key got %s"
|
||||
errInvalidSecretType = "ESO can only push/delete %s record types. Secret %s is type %s"
|
||||
errInvalidSecretType = "ESO can only push/delete records of type %s. Secret %s is type %s"
|
||||
errFieldNotFound = "secret %s does not contain any custom field with label %s"
|
||||
|
||||
externalSecretType = "externalSecrets"
|
||||
|
@ -66,6 +66,7 @@ type Client struct {
|
|||
type SecurityClient interface {
|
||||
GetSecrets(filter []string) ([]*ksm.Record, error)
|
||||
GetSecretByTitle(recordTitle string) (*ksm.Record, error)
|
||||
GetSecretsByTitle(recordTitle string) (records []*ksm.Record, err error)
|
||||
CreateSecretWithRecordData(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error)
|
||||
DeleteSecrets(recrecordUids []string) (map[string]string, error)
|
||||
Save(record *ksm.Record) error
|
||||
|
@ -172,24 +173,22 @@ func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1b
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
record, err := c.findSecretByName(parts[0])
|
||||
if err != nil {
|
||||
_, err = c.createSecret(parts[0], parts[1], value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if record != nil {
|
||||
if record.Type() != externalSecretType {
|
||||
return fmt.Errorf(errInvalidSecretType, externalSecretType, record.Title(), record.Type())
|
||||
}
|
||||
err = c.updateSecret(record, parts[1], value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
if record != nil {
|
||||
if record.Type() == externalSecretType {
|
||||
return c.updateSecret(record, parts[1], value)
|
||||
} else {
|
||||
return fmt.Errorf(errInvalidSecretType, externalSecretType, record.Title(), record.Type())
|
||||
}
|
||||
} else {
|
||||
_, err = c.createSecret(parts[0], parts[1], value)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) DeleteSecret(_ context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
|
||||
|
@ -200,16 +199,15 @@ func (c *Client) DeleteSecret(_ context.Context, remoteRef esv1beta1.PushSecretR
|
|||
secret, err := c.findSecretByName(parts[0])
|
||||
if err != nil {
|
||||
return err
|
||||
} else if secret == nil {
|
||||
return nil // not found == already deleted (success)
|
||||
}
|
||||
|
||||
if secret.Type() != externalSecretType {
|
||||
return fmt.Errorf(errInvalidSecretType, externalSecretType, secret.Title(), secret.Type())
|
||||
}
|
||||
_, err = c.ksmClient.DeleteSecrets([]string{secret.Uid})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
|
||||
|
@ -325,12 +323,32 @@ func (c *Client) findSecretByID(id string) (*ksm.Record, error) {
|
|||
}
|
||||
|
||||
func (c *Client) findSecretByName(name string) (*ksm.Record, error) {
|
||||
record, err := c.ksmClient.GetSecretByTitle(name)
|
||||
records, err := c.ksmClient.GetSecretsByTitle(name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return record, nil
|
||||
// filter in-place, preserve only records of type externalSecretType
|
||||
n := 0
|
||||
for _, record := range records {
|
||||
if record.Type() == externalSecretType {
|
||||
records[n] = record
|
||||
n++
|
||||
}
|
||||
}
|
||||
records = records[:n]
|
||||
|
||||
// record not found is not an error - handled differently:
|
||||
// PushSecret will create new record instead
|
||||
// DeleteSecret will consider record already deleted (no error)
|
||||
if len(records) == 0 {
|
||||
return nil, nil
|
||||
} else if len(records) == 1 {
|
||||
return records[0], nil
|
||||
}
|
||||
|
||||
// len(records) > 1
|
||||
return nil, fmt.Errorf(errKeeperSecuritySecretNotUnique, name)
|
||||
}
|
||||
|
||||
func (s *Secret) validate() error {
|
||||
|
|
|
@ -70,8 +70,8 @@ func TestClientDeleteSecret(t *testing.T) {
|
|||
record0: record0,
|
||||
}, nil
|
||||
},
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
return generateRecords()[0], nil
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return generateRecords()[:1], nil
|
||||
},
|
||||
},
|
||||
folderID: folderID,
|
||||
|
@ -85,11 +85,16 @@ func TestClientDeleteSecret(t *testing.T) {
|
|||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Delete invalid secret type",
|
||||
name: "Delete secret with multiple matches by Name",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
return generateRecords()[1], nil
|
||||
DeleteSecretsFn: func(recrecordUids []string) (map[string]string, error) {
|
||||
return map[string]string{
|
||||
record0: record0,
|
||||
}, nil
|
||||
},
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return []*ksm.Record{generateRecords()[0], generateRecords()[0]}, nil
|
||||
},
|
||||
},
|
||||
folderID: folderID,
|
||||
|
@ -106,7 +111,7 @@ func TestClientDeleteSecret(t *testing.T) {
|
|||
name: "Delete non existing secret",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return nil, errors.New("failed")
|
||||
},
|
||||
},
|
||||
|
@ -303,6 +308,24 @@ func TestClientGetSecret(t *testing.T) {
|
|||
want: []byte(outputRecord0),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Get secret with multiple matches by ID",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretsFn: func(filter []string) ([]*ksm.Record, error) {
|
||||
return []*ksm.Record{generateRecords()[0], generateRecords()[0]}, nil
|
||||
},
|
||||
},
|
||||
folderID: folderID,
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
ref: v1beta1.ExternalSecretDataRemoteRef{
|
||||
Key: record0,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "Get non existing secret",
|
||||
fields: fields{
|
||||
|
@ -511,8 +534,8 @@ func TestClientPushSecret(t *testing.T) {
|
|||
name: "Push new valid secret",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
return nil, errors.New("NotFound")
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return generateRecords()[0:0], nil
|
||||
},
|
||||
CreateSecretWithRecordDataFn: func(recUID, folderUid string, recordData *ksm.RecordCreate) (string, error) {
|
||||
return "record5", nil
|
||||
|
@ -533,8 +556,8 @@ func TestClientPushSecret(t *testing.T) {
|
|||
name: "Push existing valid secret",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
return generateRecords()[0], nil
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return generateRecords()[0:1], nil
|
||||
},
|
||||
SaveFn: func(record *ksm.Record) error {
|
||||
return nil
|
||||
|
@ -552,14 +575,11 @@ func TestClientPushSecret(t *testing.T) {
|
|||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "Push existing invalid secret",
|
||||
name: "Unable to push new valid secret with multiple matches by Name",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
return generateRecords()[1], nil
|
||||
},
|
||||
SaveFn: func(record *ksm.Record) error {
|
||||
return nil
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return []*ksm.Record{generateRecords()[0], generateRecords()[0]}, nil
|
||||
},
|
||||
},
|
||||
folderID: folderID,
|
||||
|
@ -569,7 +589,7 @@ func TestClientPushSecret(t *testing.T) {
|
|||
SecretKey: secretKey,
|
||||
RemoteKey: validExistingRecord,
|
||||
},
|
||||
value: []byte("foo2"),
|
||||
value: []byte("foo"),
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
|
@ -577,7 +597,7 @@ func TestClientPushSecret(t *testing.T) {
|
|||
name: "Unable to push new valid secret",
|
||||
fields: fields{
|
||||
ksmClient: &fake.MockKeeperClient{
|
||||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return nil, errors.New("NotFound")
|
||||
},
|
||||
CreateSecretWithRecordDataFn: func(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error) {
|
||||
|
@ -602,6 +622,9 @@ func TestClientPushSecret(t *testing.T) {
|
|||
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
|
||||
return generateRecords()[0], nil
|
||||
},
|
||||
GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return generateRecords()[0:1], nil
|
||||
},
|
||||
SaveFn: func(record *ksm.Record) error {
|
||||
return errors.New("Unable to save")
|
||||
},
|
||||
|
|
|
@ -19,6 +19,7 @@ import ksm "github.com/keeper-security/secrets-manager-go/core"
|
|||
type MockKeeperClient struct {
|
||||
GetSecretsFn func([]string) ([]*ksm.Record, error)
|
||||
GetSecretByTitleFn func(recordTitle string) (*ksm.Record, error)
|
||||
GetSecretsByTitleFn func(recordTitle string) (records []*ksm.Record, err error)
|
||||
CreateSecretWithRecordDataFn func(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error)
|
||||
DeleteSecretsFn func(recrecordUids []string) (map[string]string, error)
|
||||
SaveFn func(record *ksm.Record) error
|
||||
|
@ -47,6 +48,10 @@ func (mc *MockKeeperClient) GetSecretByTitle(recordTitle string) (*ksm.Record, e
|
|||
return mc.GetSecretByTitleFn(recordTitle)
|
||||
}
|
||||
|
||||
func (mc *MockKeeperClient) GetSecretsByTitle(recordTitle string) (records []*ksm.Record, err error) {
|
||||
return mc.GetSecretsByTitleFn(recordTitle)
|
||||
}
|
||||
|
||||
func (mc *MockKeeperClient) CreateSecretWithRecordData(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error) {
|
||||
return mc.CreateSecretWithRecordDataFn(recUID, folderUID, recordData)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue