Browse Source

feat: 1password find by tags (#4476)

Signed-off-by: Gustavo Carvalho <gusfcarvalho@gmail.com>
Co-authored-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Gustavo Fernandes de Carvalho 1 year ago
parent
commit
4876805a52

+ 1 - 1
docs/provider/1password-automation.md

@@ -27,7 +27,7 @@ _**The 1Password API calls the entries in vaults 'Items'. These docs use the sam
 * `dataFrom`:
     * `find.path` is equated to Item Title.
     * `find.name.regexp` is equated to field Labels.
-    * `find.tags` are not supported at this time.
+    * `find.tags` fetches for Items with the same tags matching the keys of `find.tags`.
 
 ### Prerequisites
 

+ 41 - 5
pkg/provider/onepassword/onepassword.go

@@ -19,6 +19,7 @@ import (
 	"errors"
 	"fmt"
 	"net/url"
+	"slices"
 	"sort"
 	"strings"
 	"time"
@@ -438,10 +439,6 @@ func (provider *ProviderOnePassword) GetSecretMap(_ context.Context, ref esv1bet
 
 // GetAllSecrets syncs multiple 1Password Items into a single Kubernetes Secret, for dataFrom.find.
 func (provider *ProviderOnePassword) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
-	if ref.Tags != nil {
-		return nil, errors.New(errTagsNotImplemented)
-	}
-
 	secretData := make(map[string][]byte)
 	sortedVaults := sortVaults(provider.vaults)
 	for _, vaultName := range sortedVaults {
@@ -449,7 +446,13 @@ func (provider *ProviderOnePassword) GetAllSecrets(_ context.Context, ref esv1be
 		if err != nil {
 			return nil, fmt.Errorf(errGetVault, err)
 		}
-
+		if ref.Tags != nil {
+			err = provider.getAllByTags(vault.ID, ref, secretData)
+			if err != nil {
+				return nil, err
+			}
+			return secretData, nil
+		}
 		err = provider.getAllForVault(vault.ID, ref, secretData)
 		if err != nil {
 			return nil, err
@@ -610,6 +613,39 @@ func (provider *ProviderOnePassword) getAllFiles(item onepassword.Item, ref esv1
 	return nil
 }
 
+func (provider *ProviderOnePassword) getAllByTags(vaultID string, ref esv1beta1.ExternalSecretFind, secretData map[string][]byte) error {
+	items, err := provider.client.GetItems(vaultID)
+	if err != nil {
+		return fmt.Errorf(errGetItem, err)
+	}
+	for _, item := range items {
+		if !checkTags(ref.Tags, item.Tags) {
+			continue
+		}
+		// handle files
+		err = provider.getAllFiles(item, ref, secretData)
+		if err != nil {
+			return err
+		}
+		// handle fields
+		err = provider.getAllFields(item, ref, secretData)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+// checkTags verifies that all elements in source are present in target.
+func checkTags(source map[string]string, target []string) bool {
+	for s := range source {
+		if !slices.Contains(target, s) {
+			return false
+		}
+	}
+	return true
+}
+
 func (provider *ProviderOnePassword) getAllForVault(vaultID string, ref esv1beta1.ExternalSecretFind, secretData map[string][]byte) error {
 	items, err := provider.client.GetItems(vaultID)
 	if err != nil {

+ 67 - 6
pkg/provider/onepassword/onepassword_test.go

@@ -1121,7 +1121,6 @@ func TestGetAllSecrets(t *testing.T) {
 		provider  *ProviderOnePassword
 		checks    []check
 	}
-
 	testCases := []testCase{
 		{
 			setupNote: "three vaults, three items, all different field Labels",
@@ -1209,17 +1208,79 @@ func TestGetAllSecrets(t *testing.T) {
 					expectedMap: map[string][]byte{},
 					expectedErr: nil,
 				},
+			},
+		},
+		{
+			setupNote: "one vault, three items, find by tags",
+			provider: &ProviderOnePassword{
+				vaults: map[string]int{myVault: 1},
+				client: fake.NewMockClient().
+					AddPredictableVault(myVault).
+					AppendItem(myVaultID, onepassword.Item{
+						ID:    myItemID,
+						Title: myItem,
+						Tags:  []string{"foo", "bar"},
+						Vault: onepassword.ItemVault{ID: myVaultID},
+					}).
+					AppendItemField(myVaultID, myItemID, onepassword.ItemField{
+						Label: key1,
+						Value: value1,
+					}).
+					AppendItemField(myVaultID, myItemID, onepassword.ItemField{
+						Label: key2,
+						Value: value2,
+					}).
+					AppendItem(myVaultID, onepassword.Item{
+						ID:    "my-item-id-2",
+						Title: "my-item-2",
+						Vault: onepassword.ItemVault{ID: myVaultID},
+						Tags:  []string{"foo", "baz"},
+					}).
+					AppendItemField(myVaultID, "my-item-id-2", onepassword.ItemField{
+						Label: key3,
+						Value: value3,
+					}).
+					AppendItem(myVaultID, onepassword.Item{
+						ID:    "my-item-id-3",
+						Title: "my-item-3",
+						Vault: onepassword.ItemVault{ID: myVaultID},
+						Tags:  []string{"bang", "bing"},
+					}).
+					AppendItemField(myVaultID, "my-item-id-3", onepassword.ItemField{
+						Label: key4,
+						Value: value4,
+					}),
+			},
+			checks: []check{
 				{
-					checkNote: "error when find.tags",
+					checkNote: "find with tags",
 					ref: esv1beta1.ExternalSecretFind{
-						Name: &esv1beta1.FindName{
-							RegExp: "key*",
+						Path: pointer.To(myItem),
+						Tags: map[string]string{
+							"foo": "true",
+							"bar": "true",
 						},
+					},
+					expectedMap: map[string][]byte{
+						key1: []byte(value1),
+						key2: []byte(value2),
+					},
+					expectedErr: nil,
+				},
+				{
+					checkNote: "find with tags and get all",
+					ref: esv1beta1.ExternalSecretFind{
+						Path: pointer.To(myItem),
 						Tags: map[string]string{
-							"asdf": "fdas",
+							"foo": "true",
 						},
 					},
-					expectedErr: errors.New(errTagsNotImplemented),
+					expectedMap: map[string][]byte{
+						key1: []byte(value1),
+						key2: []byte(value2),
+						key3: []byte(value3),
+					},
+					expectedErr: nil,
 				},
 			},
 		},