package connect import ( "context" "errors" "fmt" "time" "github.com/1Password/connect-sdk-go/connect" "github.com/1Password/connect-sdk-go/onepassword" "github.com/1Password/onepassword-operator/pkg/onepassword/model" ) // Config holds the configuration for the Connect client. type Config struct { ConnectHost string ConnectToken string } // Connect is a client for interacting with 1Password using the Connect API. type Connect struct { client connect.Client } // NewClient creates a new Connect client using provided configuration. func NewClient(config Config) *Connect { return &Connect{ client: connect.NewClient(config.ConnectHost, config.ConnectToken), } } func (c *Connect) GetItemByID(ctx context.Context, vaultID, itemID string) (*model.Item, error) { connectItem, err := c.client.GetItemByUUID(itemID, vaultID) if err != nil { return nil, fmt.Errorf("failed to GetItemByID using 1Password Connect: %w", err) } var item model.Item item.FromConnectItem(connectItem) return &item, nil } func (c *Connect) GetItemsByTitle(ctx context.Context, vaultID, itemTitle string) ([]model.Item, error) { // Get all items in the vault with the specified title connectItems, err := c.client.GetItemsByTitle(itemTitle, vaultID) if err != nil { return nil, fmt.Errorf("failed to GetItemsByTitle using 1Password Connect: %w", err) } items := make([]model.Item, len(connectItems)) for i, connectItem := range connectItems { var item model.Item item.FromConnectItem(&connectItem) items[i] = item } return items, nil } // GetFileContent retrieves the content of a file from a 1Password item. // As the Connect has a delay when synchronizing files and returns a 500 error in this case, // this function implements a retry mechanism. func (c *Connect) GetFileContent(ctx context.Context, vaultID, itemID, fileID string) ([]byte, error) { const maxRetries = 5 const delay = 1 * time.Second var lastErr error for i := 0; i < maxRetries; i++ { bytes, err := c.client.GetFileContent(&onepassword.File{ ContentPath: fmt.Sprintf("/v1/vaults/%s/items/%s/files/%s/content", vaultID, itemID, fileID), }) if err == nil { return bytes, nil } var connectErr *onepassword.Error if errors.As(err, &connectErr) && connectErr.StatusCode == 500 { lastErr = err time.Sleep(delay) continue } return nil, fmt.Errorf("failed to GetFileContent using 1Password Connect: %w", err) } return nil, fmt.Errorf("failed to GetFileContent using 1Password Connect after %d retries: %w", maxRetries, lastErr) } func (c *Connect) GetVaultsByTitle(ctx context.Context, vaultQuery string) ([]model.Vault, error) { connectVaults, err := c.client.GetVaultsByTitle(vaultQuery) if err != nil { return nil, fmt.Errorf("failed to GetVaultsByTitle using 1Password Connect: %w", err) } var vaults []model.Vault for _, connectVault := range connectVaults { if vaultQuery == connectVault.Name { var vault model.Vault vault.FromConnectVault(&connectVault) vaults = append(vaults, vault) } } return vaults, nil }