Allow vault and item titles in item path

Items can now be accessed by either vaults/<vault_id>/items/<item_id> or vaults/<vault_title>/items/<item_title>
This commit is contained in:
jillianwilson
2020-12-17 16:31:48 -04:00
parent 824f54b4fa
commit dae6e51112
9 changed files with 176 additions and 28 deletions

View File

@@ -5,23 +5,27 @@ import (
)
type TestClient struct {
GetVaultsFunc func() ([]onepassword.Vault, error)
GetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
GetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
GetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error)
CreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
UpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
DeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
GetVaultsFunc func() ([]onepassword.Vault, error)
GetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error)
GetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
GetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
GetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error)
GetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error)
CreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
UpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
DeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
}
var (
GetGetVaultsFunc func() ([]onepassword.Vault, error)
GetGetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
DoGetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error)
DoCreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
DoDeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
DoGetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
DoUpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
GetGetVaultsFunc func() ([]onepassword.Vault, error)
DoGetVaultsByTitleFunc func(title string) ([]onepassword.Vault, error)
GetGetItemFunc func(uuid string, vaultUUID string) (*onepassword.Item, error)
DoGetItemsByTitleFunc func(title string, vaultUUID string) ([]onepassword.Item, error)
DoGetItemByTitleFunc func(title string, vaultUUID string) (*onepassword.Item, error)
DoCreateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
DoDeleteItemFunc func(item *onepassword.Item, vaultUUID string) error
DoGetItemsFunc func(vaultUUID string) ([]onepassword.Item, error)
DoUpdateItemFunc func(item *onepassword.Item, vaultUUID string) (*onepassword.Item, error)
)
// Do is the mock client's `Do` func
@@ -29,6 +33,10 @@ func (m *TestClient) GetVaults() ([]onepassword.Vault, error) {
return GetGetVaultsFunc()
}
func (m *TestClient) GetVaultsByTitle(title string) ([]onepassword.Vault, error) {
return DoGetVaultsByTitleFunc(title)
}
func (m *TestClient) GetItem(uuid string, vaultUUID string) (*onepassword.Item, error) {
return GetGetItemFunc(uuid, vaultUUID)
}
@@ -37,6 +45,10 @@ func (m *TestClient) GetItems(vaultUUID string) ([]onepassword.Item, error) {
return DoGetItemsFunc(vaultUUID)
}
func (m *TestClient) GetItemsByTitle(title, vaultUUID string) ([]onepassword.Item, error) {
return DoGetItemsByTitleFunc(title, vaultUUID)
}
func (m *TestClient) GetItemByTitle(title string, vaultUUID string) (*onepassword.Item, error) {
return DoGetItemByTitleFunc(title, vaultUUID)
}

View File

@@ -6,13 +6,26 @@ import (
"github.com/1Password/connect-sdk-go/connect"
"github.com/1Password/connect-sdk-go/onepassword"
logf "sigs.k8s.io/controller-runtime/pkg/log"
)
var logger = logf.Log.WithName("retrieve_item")
func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*onepassword.Item, error) {
vaultId, itemId, err := ParseVaultIdAndItemIdFromPath(path)
vaultValue, itemValue, err := ParseVaultAndItemFromPath(path)
if err != nil {
return nil, err
}
vaultId, err := getVaultId(opConnectClient, vaultValue)
if err != nil {
return nil, err
}
itemId, err := getItemId(opConnectClient, itemValue, vaultId)
if err != nil {
return nil, err
}
item, err := opConnectClient.GetItem(itemId, vaultId)
if err != nil {
return nil, err
@@ -20,10 +33,60 @@ func GetOnePasswordItemByPath(opConnectClient connect.Client, path string) (*one
return item, nil
}
func ParseVaultIdAndItemIdFromPath(path string) (string, string, error) {
func ParseVaultAndItemFromPath(path string) (string, string, error) {
splitPath := strings.Split(path, "/")
if len(splitPath) == 4 && splitPath[0] == "vaults" && splitPath[2] == "items" {
return splitPath[1], splitPath[3], nil
}
return "", "", fmt.Errorf("%q is not an acceptable path for One Password item. Must be of the format: `vaults/{vault_id}/items/{item_id}`", path)
}
func getVaultId(client connect.Client, vaultIdentifier string) (string, error) {
if !IsValidClientUUID(vaultIdentifier) {
vaults, err := client.GetVaultsByTitle(vaultIdentifier)
if err != nil {
return "", err
}
if len(vaults) == 0 {
return "", fmt.Errorf("No vaults found with identifier %q", vaultIdentifier)
}
oldestVault := vaults[0]
if len(vaults) > 1 {
for _, returnedVault := range vaults {
if returnedVault.CreatedAt.Before(oldestVault.CreatedAt) {
oldestVault = returnedVault
}
}
logger.Info(fmt.Sprintf("%v 1Password vaults found with the title %q. Will use vault %q as it is the oldest.", len(vaults), vaultIdentifier, oldestVault.ID))
}
vaultIdentifier = oldestVault.ID
}
return vaultIdentifier, nil
}
func getItemId(client connect.Client, itemIdentifier string, vaultId string) (string, error) {
if !IsValidClientUUID(itemIdentifier) {
items, err := client.GetItemsByTitle(itemIdentifier, vaultId)
if err != nil {
return "", err
}
if len(items) == 0 {
return "", fmt.Errorf("No items found with identifier %q", itemIdentifier)
}
oldestItem := items[0]
if len(items) > 1 {
for _, returnedItem := range items {
if returnedItem.CreatedAt.Before(oldestItem.CreatedAt) {
oldestItem = returnedItem
}
}
logger.Info(fmt.Sprintf("%v 1Password items found with the title %q. Will use item %q as it is the oldest.", len(items), itemIdentifier, oldestItem.ID))
}
itemIdentifier = oldestItem.ID
}
return itemIdentifier, nil
}

20
pkg/onepassword/uuid.go Normal file
View File

@@ -0,0 +1,20 @@
package onepassword
// UUIDLength defines the required length of UUIDs
const UUIDLength = 26
// IsValidClientUUID returns true if the given client uuid is valid.
func IsValidClientUUID(uuid string) bool {
if len(uuid) != UUIDLength {
return false
}
for _, c := range uuid {
valid := (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')
if !valid {
return false
}
}
return true
}